Was sind all die allgemeinen undefinierten Verhaltensweisen, die ein C++-Programmierer kennen sollte?
Sagen Sie, wie:
a[i] = i++;
jaraaj
Was sind all die allgemeinen undefinierten Verhaltensweisen, die ein C++-Programmierer kennen sollte?
Sagen Sie, wie:
a[i] = i++;
NULL
Zeigermemcpy
um überlappende Puffer zu kopieren.int64_t i = 1; i <<= 72
ist nicht definiert)int i; i++; cout << i;
)volatile
oder sig_atomic_t
beim Empfang eines Signalslong int
#if
AusdruckHm … NaN (x / 0) und Infinity (0 / 0) wurden von IEE 754 abgedeckt, wenn C++ später entworfen wurde, warum zeichnet es x / 0 als undefiniert auf?
– neu123456
5. April 2011 um 13:22 Uhr
Betreff: “Ein umgekehrter Schrägstrich, gefolgt von einem Zeichen, das nicht Teil der angegebenen Escape-Codes in einer Zeichen- oder Zeichenfolgenkonstante ist.” Das ist UB in C89 (§3.1.3.4) und C++03 (das C89 enthält), aber nicht in C99. C99 sagt, dass “das Ergebnis kein Token ist und eine Diagnose erforderlich ist” (§6.4.4.4). Vermutlich wird C++0x (das C89 enthält) dasselbe sein.
– Adam Rosenfield
7. Juni 2011 um 4:31 Uhr
Der C99-Standard enthält eine Liste undefinierter Verhaltensweisen in Anhang J.2. Es würde einige Arbeit erfordern, diese Liste an C++ anzupassen. Sie müssten die Verweise auf die richtigen C++-Klauseln anstatt auf die C99-Klauseln ändern, alles Unwichtige entfernen und auch prüfen, ob all diese Dinge wirklich sowohl in C++ als auch in C undefiniert sind. Aber es bietet einen Anfang.
– Steve Jessop
7. Juni 2011 um 8:13 Uhr
@ new123456 – nicht alle Gleitkommaeinheiten sind IEE754-kompatibel. Wenn C++ die IEE754-Konformität erfordert, müssten Compiler den Fall, in dem RHS null ist, über eine explizite Prüfung testen und behandeln. Indem das Verhalten undefiniert gemacht wird, kann der Compiler diesen Overhead vermeiden, indem er sagt: “Wenn Sie eine Nicht-IEE754-FPU verwenden, erhalten Sie kein IEEE754-FPU-Verhalten”.
– SicherheitsMatt
1. September 2012 um 14:18 Uhr
“Auswerten eines Ausdrucks, dessen Ergebnis nicht im Bereich der entsprechenden Typen liegt” …. ganzzahliger Überlauf ist für UNSIGNED ganzzahlige Typen wohldefiniert, nur nicht für vorzeichenbehaftete.
– nacitar sevaht
9. August 2013 um 20:06 Uhr
Martin York
Die Reihenfolge, in der Funktionsparameter ausgewertet werden, ist nicht spezifiziert Verhalten. (Dies wird Ihr Programm nicht zum Absturz bringen, explodieren oder Pizza bestellen … im Gegensatz zu nicht definiert Verhalten.)
Die einzige Voraussetzung ist, dass alle Parameter vollständig ausgewertet werden müssen, bevor die Funktion aufgerufen wird.
Dies:
// The simple obvious one.
callFunc(getA(),getB());
Kann äquivalent dazu sein:
int a = getA();
int b = getB();
callFunc(a,b);
Oder dieses:
int b = getB();
int a = getA();
callFunc(a,b);
Es kann beides sein; es liegt am Compiler. Das Ergebnis kann abhängig von den Nebenwirkungen von Bedeutung sein.
Die Reihenfolge ist unspezifiziert, nicht undefiniert.
– Rob Kennedy
15. Dezember 2008 um 7:55 Uhr
Ich hasse diesen 🙂 Ich habe einen Tag Arbeit verloren, als ich einen dieser Fälle aufgespürt habe … jedenfalls habe ich meine Lektion gelernt und bin zum Glück nicht wieder gestürzt
– Robert Gould
15. Dezember 2008 um 8:29 Uhr
@Rob: Ich würde mit Ihnen über die Bedeutungsänderung hier streiten, aber ich weiß, dass das Standardkomitee bei der genauen Definition dieser beiden Wörter sehr wählerisch ist. Also werde ich es einfach ändern 🙂
– Martin York
15. Dezember 2008 um 8:34 Uhr
Bei diesem hatte ich Glück. Ich wurde davon gebissen, als ich auf dem College war und einen Professor hatte, der einen Blick darauf warf und mir mein Problem in etwa 5 Sekunden erklärte. Ich weiß nicht, wie viel Zeit ich sonst mit dem Debuggen verschwendet hätte.
– Bill die Eidechse
15. Dezember 2008 um 16:06 Uhr
Martin York
Dem Compiler steht es frei, die Bewertungsteile eines Ausdrucks neu zu ordnen (unter der Annahme, dass die Bedeutung unverändert bleibt).
Aus der ursprünglichen Frage:
a[i] = i++;
// This expression has three parts:
(a) a[i]
(b) i++
(c) Assign (b) to (a)
// (c) is guaranteed to happen after (a) and (b)
// But (a) and (b) can be done in either order.
// See n2521 Section 5.17
// (b) increments i but returns the original value.
// See n2521 Section 5.2.6
// Thus this expression can be written as:
int rhs = i++;
int lhs& = a[i];
lhs = rhs;
// or
int lhs& = a[i];
int rhs = i++;
lhs = rhs;
Doppelt geprüfte Verriegelung. Und ein einfacher Fehler zu machen.
A* a = new A("plop");
// Looks simple enough.
// But this can be split into three parts.
(a) allocate Memory
(b) Call constructor
(c) Assign value to 'a'
// No problem here:
// The compiler is allowed to do this:
(a) allocate Memory
(c) Assign value to 'a'
(b) Call constructor.
// This is because the whole thing is between two sequence points.
// So what is the big deal.
// Simple Double checked lock. (I know there are many other problems with this).
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
a = new A("Plop"); // (Point A).
}
}
a->doStuff();
// Think of this situation.
// Thread 1: Reaches point A. Executes (a)(c)
// Thread 1: Is about to do (b) and gets unscheduled.
// Thread 2: Reaches point B. It can now skip the if block
// Remember (c) has been done thus 'a' is not NULL.
// But the memory has not been initialized.
// Thread 2 now executes doStuff() on an uninitialized variable.
// The solution to this problem is to move the assignment of 'a'
// To the other side of the sequence point.
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
A* tmp = new A("Plop"); // (Point A).
a = tmp;
}
}
a->doStuff();
// Of course there are still other problems because of C++ support for
// threads. But hopefully these are addresses in the next standard.
Was ist mit Sequenzpunkt gemeint?
– jaraaj
15. Dezember 2008 um 8:25 Uhr
Ooh … das ist böse, zumal ich gesehen habe, dass genau diese Struktur in Java empfohlen wird
– Tom
15. Dezember 2008 um 14:44 Uhr
Beachten Sie, dass einige Compiler das Verhalten in dieser Situation definieren. In VC++ 2005+ werden beispielsweise, wenn a flüchtig ist, die erforderlichen Speicherbarrieren eingerichtet, um eine Neuordnung von Anweisungen zu verhindern, sodass doppelt geprüfte Sperren funktionieren.
– Sonnenfinsternis
23. Juni 2009 um 16:11 Uhr
Martin York: // (c) wird garantiert nach (a) und (b) passieren Ist es das? Zugegebenermaßen wäre in diesem speziellen Beispiel das einzige Szenario, in dem es von Bedeutung sein könnte, wenn „i“ eine flüchtige Variable wäre, die einem Hardwareregister zugeordnet ist, und a[i] (alter Wert von ‘i’) wurde mit einem Alias versehen, aber gibt es eine Garantie dafür, dass das Inkrement vor einem Sequenzpunkt erfolgt?
– Superkatze
14. August 2010 um 20:18 Uhr
Mein Favorit ist “Unendliche Rekursion bei der Instanziierung von Vorlagen”, weil ich glaube, dass dies die einzige ist, bei der das undefinierte Verhalten zur Kompilierzeit auftritt.
jaraaj
Zuweisung an eine Konstante nach dem Strippen const
ness verwenden const_cast<>
:
const int i = 10;
int *p = const_cast<int*>( &i );
*p = 1234; //Undefined
Gemeinschaft
neben undefiniertes Verhaltengibt es auch das ebenso fiese implementierungsdefiniertes Verhalten.
Undefiniertes Verhalten tritt auf, wenn ein Programm etwas tut, dessen Ergebnis nicht durch den Standard spezifiziert ist.
Implementierungsdefiniertes Verhalten ist eine Aktion eines Programms, dessen Ergebnis nicht durch den Standard definiert ist, die Implementierung jedoch dokumentieren muss. Ein Beispiel ist “Multibyte-Zeichenliterale” aus der Stack Overflow-Frage Gibt es einen C-Compiler, der dies nicht kompiliert?.
Implementierungsdefiniertes Verhalten beißt Sie nur, wenn Sie mit der Portierung beginnen (aber ein Upgrade auf eine neue Version des Compilers ist auch eine Portierung!)
Peter Mortensen
Variablen dürfen in einem Ausdruck nur einmal aktualisiert werden (technisch einmal zwischen Sequenzpunkten).
int i =1;
i = ++i;
// Undefined. Assignment to 'i' twice in the same expression.
In der Tat wenigstens einmal zwischen zwei Sequenzpunkten.
– Prasun Saurav
12. August 2010 um 13:10 Uhr
@Prasoon: Ich glaube du meintest: maximal einmal zwischen zwei Sequenzpunkten. 🙂
– Nawaz
7. Juni 2011 um 9:24 Uhr
Bist du sicher. Das sieht gut definiert aus.
– Martin York
15. Dezember 2008 um 7:10 Uhr
6.2.2 Bewertungsauftrag [expr.evaluation] in der Programmiersprache C ++ so sagen. Ich habe keine andere Referenz
– jaraaj
15. Dezember 2008 um 7:24 Uhr
Er hat recht.. hat sich gerade 6.2.2 in The C++ Programming Language angesehen und da steht v[i] = i++ ist undefiniert
– Dancavallaro
15. Dezember 2008 um 7:24 Uhr
Ich würde mir vorstellen, dass der Compiler das i ++ vor oder nach der Berechnung des Speicherplatzes von v ausführt[i]. Sicher, ich werde immer dort eingesetzt werden. aber es könnte entweder an v schreiben[i] oder V[i+1] je nach Reihenfolge der Operationen..
– Evan Teran
15. Dezember 2008 um 7:44 Uhr
Alles, was die Programmiersprache C++ sagt, ist: „Die Reihenfolge der Operationen von Teilausdrücken innerhalb eines Ausdrucks ist undefiniert. Insbesondere können Sie nicht davon ausgehen, dass der Ausdruck von links nach rechts ausgewertet wird.“
– Dancavallaro
15. Dezember 2008 um 8:14 Uhr