Warum sind diese Konstrukte mit Hilfe von pre-und post-Inkrement Undefiniertes Verhalten?
#include <stdio.h>
int main(void)
{
int i = 0;
i = i++ + ++i;
printf("%d\n", i); //3
i = 1;
i = (i++);
printf("%d\n", i); //2 Should be 1, no ?
volatile int u = 0;
u = u++ + ++u;
printf("%d\n", u); //1
u = 1;
u = (u++);
printf("%d\n", u); //2 Should also be one, no ?
register int v = 0;
v = v++ + ++v;
printf("%d\n", v); //3 (Should be the same as u ?)
int w = 0;
printf("%d %d %d\n", w++, ++w, w); //shouldn't this print 0 2 2
int x[2] = { 5, 8 }, y = 0;
x[y] = y ++;
printf("%d %d\n", x[0], x[1]); //shouldn't this print 0 8? or 5 0?
}
Hausaufgaben? Nicht versuchen, ein Schmerz sein, aber man sollte Sie nie code schreiben, der mit Ausdrücken, wie diese. Sie sind in der Regel als Akademische Beispiele, manchmal zeigt Sie, dass verschiedene Compiler unterschiedliche Ertrags-Ausgang.
NÖ, brauchte nur einige Hinweise auf "sequence points". Während der Arbeit fand ich ein Stück code mit i = i++, I thougth "Dies nicht geändert wird, wird der Wert von i". Getestet habe ich und ich fragte mich, warum. Da habe ich entfernt, diese Aussage und ersetzt es durch i++;
Erklären Sie diese undefinierten Verhalten? Erklären Sie, was über Sie? Wie Sie sich Verhalten ist nicht definiert.
Ich denke, es ist interessant, dass jeder IMMER davon ausgegangen, dass Fragen wie diese sind gefragt, denn der Fragesteller will das Konstrukt in Frage. Meine erste Vermutung war, dass PiX weiß, dass diese schlecht sind, aber neugierig ist, warum das Verhalten so ist, wie die auf whataver compiler-er/Sie war... Und ja, was entspannen Sie sagte... es ist nicht definiert, es könnte nichts tun... einschließlich JCF (Springen und Feuer Fangen)
Ich bin neugierig: Warum nicht den Compiler scheinen zu warnen, auf Konstrukte wie "u = u++ + ++u;" wenn, ist das Ergebnis undefiniert?
NÖ, brauchte nur einige Hinweise auf "sequence points". Während der Arbeit fand ich ein Stück code mit i = i++, I thougth "Dies nicht geändert wird, wird der Wert von i". Getestet habe ich und ich fragte mich, warum. Da habe ich entfernt, diese Aussage und ersetzt es durch i++;
Erklären Sie diese undefinierten Verhalten? Erklären Sie, was über Sie? Wie Sie sich Verhalten ist nicht definiert.
Ich denke, es ist interessant, dass jeder IMMER davon ausgegangen, dass Fragen wie diese sind gefragt, denn der Fragesteller will das Konstrukt in Frage. Meine erste Vermutung war, dass PiX weiß, dass diese schlecht sind, aber neugierig ist, warum das Verhalten so ist, wie die auf whataver compiler-er/Sie war... Und ja, was entspannen Sie sagte... es ist nicht definiert, es könnte nichts tun... einschließlich JCF (Springen und Feuer Fangen)
Ich bin neugierig: Warum nicht den Compiler scheinen zu warnen, auf Konstrukte wie "u = u++ + ++u;" wenn, ist das Ergebnis undefiniert?
InformationsquelleAutor PiX | 2009-06-04
Du musst angemeldet sein, um einen Kommentar abzugeben.
C hat das Konzept der Undefiniertes Verhalten, d.h. einige Konstrukte sind syntaktisch gültig, aber Sie kann nicht Vorhersagen, das Verhalten wenn der code ausgeführt wird.
Soweit ich weiß, ist der standard nicht ausdrücklich sagen warum das Konzept der Undefiniertes Verhalten vorliegt. In meinem Kopf, es ist einfach, weil die Sprache, die Designer wollten es zu sein, einen gewissen Spielraum in der Semantik statt, D. H. die Forderung, dass alle Implementierungen Griff integer-überlauf in der exakt gleichen Art und Weise, die würde sehr wahrscheinlich zu verhängen ernsthafte performance-Kosten, die Sie gerade verlassen das Verhalten undefiniert, so dass, wenn Sie code schreiben, der bewirkt, integer-überlauf, kann alles passieren.
So, in diesem Sinne, warum sind diese "Probleme"? Die Sprache sagt ganz klar, dass bestimmte Dinge führen zu Undefiniertes Verhalten. Es ist kein problem, es gibt kein "sollte" beteiligt. Wenn das Undefinierte Verhalten ändert, wenn eine der beteiligten Variablen deklariert
volatile
, das nicht beweisen oder irgendetwas zu ändern. Es ist undefined; Sie können nicht Grund, über das Verhalten.Ihre interessantesten-Suche Beispiel, das man mit
ist ein text-Buch-Beispiel von undefiniertem Verhalten (siehe Wikipedia-Eintrag auf Reihenfolge Punkte).
Dinge, die nicht definiert sind für eine Reihe von möglichen Gründen. Dazu gehören: es gibt keine eindeutige "richtige Ergebnis", verschiedene maschinenarchitekturen stark begünstigen unterschiedliche Ergebnisse, die bestehende Praxis ist nicht konsistent sind, oder die über den Umfang der standard - (z.B. welche Dateinamen gültig sind).
Der Geist C: das Vertrauen der Programmierer... egal, wie verrückt er ist.
Nicht sicher, was du meinst. Der Begriff "undefined behavior" wird in der C-standard. Es bedeutet, dass, obwohl einige Konstrukte sind syntaktisch gültig und wird in der Regel kompilieren Sie, führen Sie zum zurücksetzen Verhalten, d.h. Sie machen keinen Sinn und sollte vermieden werden, da sich Ihr Programm wird unterbrochen, wenn es zu undefiniertem Verhalten.
das ist nur gut definiert, die in C++11 nicht in C11.
InformationsquelleAutor unwind
Einfach kompilieren und disassemblieren Ihre Zeile code, die, wenn Sie so geneigt sind, zu wissen, wie genau es ist, dass Sie bekommen, was Sie bekommen.
Dies ist, was ich auf meine Maschine, zusammen mit dem, was ich denke, ist Los:
(Ich... nehme an, dass die 0x00000014 Unterricht war eine Art von compiler-Optimierung?)
böse.c -c -o böse.bin und
gdb evil.bin
→disassemble evil
oder was auch immer die Windows-Entsprechungen von diesen sind 🙂Diese Antwort hat nicht wirklich was mit der Frage
Why are these constructs undefined behavior?
.Nebenbei, es wird einfacher sein, zu kompilieren, um die Montage (mit
gcc -S evil.c
), das ist alles, was hier gebraucht. Montage dann die Demontage ist es nur ein Kreisverkehr Weg, es zu tun.Für das Protokoll, wenn aus welchem Grund auch immer Sie sich Fragen, was ein bestimmtes Konstrukt macht -- und vor allem, wenn es gibt Verdacht, dass es möglicherweise nicht definiertes Verhalten -- der uralte Rat "versuchen Sie es einfach mit Ihrem compiler und sehen" ist potenziell sehr gefährlich. Sie werden lernen, am besten, was es tut, unter dieser version Ihrer compiler, unter diesen Umständen von heute. Sie nicht viel lernen, wenn alles, was es garantiert tun. Im Allgemeinen, "Versuch es nur mit dem compiler" führt zu nonportable-Programme, die nur mit Ihrem compiler.
InformationsquelleAutor badp
Ich denke, die relevanten Teile der C99-standard sind 6.5 Ausdrücke, §2
und 6.5.16 Zuweisungsoperatoren, §4:
soweit ich weiß
i=i=5
ist auch zu undefiniertem VerhaltenDie Begründung, die ich mag zu verwenden, für die meisten Orte gilt, dass in der Theorie ein Multi-Prozessor-Plattform umsetzen konnte so etwas wie
A=B=5;
als "Write-lock A; Write-Lock B; Speichern 5; Shop 5 zu B; Unlock B; Unock A;", und eine Aussage wieC=A+B;
als "Read-lock A; Read-lock B; Berechnen Sie A+B; Unlock A und B; Write-lock C; Store result; Unlock C;". Dies würde sicherstellen, dass, wenn ein thread hatA=B=5;
während ein anderer hatC=A+B;
letzteres thread würde entweder sehen beide schreibt so stattgefunden haben oder auch nicht. Potenziell eine nützliche Garantie. Wenn ein thread hatI=I=5;
jedoch ...... und der compiler nicht feststellen, dass beide schreibt, die waren an der gleichen Stelle (wenn eine oder beide lvalues beinhalten Zeiger, dass kann schwierig sein, zu bestimmen), der generierte code könnte deadlock. Ich glaube nicht, dass alle realen Implementierungen implementieren, die solche sperren als Teil Ihrer normalen Verhalten, aber es wäre zulässig, unter dem standard, und falls hardware, könnte die Umsetzung einer solchen Verhaltensweisen günstig kann es nützlich sein. Auf der heutigen hardware ein solches Verhalten wäre viel zu teuer zu implementieren als Standard, aber das bedeutet nicht, es wäre immer so.
aber wäre das nicht der Reihenfolge Punkt-Zugriffsregel von c99 allein ausreichen, um zu erklären, es als undefiniert Verhalten? So spielt es keine Rolle, was technisch die hardware umsetzen konnte?
InformationsquelleAutor Christoph
Kann das Verhalten nicht wirklich erklärt werden, denn es ruft sowohl unspezifische Verhalten und Undefiniertes Verhalten, so können wir keine Allgemeinen Vorhersagen über diesen code, obwohl, wenn Sie Lesen Olve Maudal gibt ' s arbeiten wie Deep C und Unspezifiziert und Undefiniert manchmal können Sie gute Vermutungen in sehr spezifischen Fällen mit einem bestimmten compiler und Umwelt, aber bitte nicht irgendwo in der Nähe der Produktion.
So bewegen Sie sich auf unspezifisch Verhalten, in Entwurf der c99-standard Abschnitt
6.5
Absatz 3 sagt(Hervorhebung von mir):Wenn wir also eine Zeile wie diese:
wissen wir nicht, ob
i++
oder++i
wird zuerst ausgewertet werden. Dies ist vor allem geben Sie die compiler bessere Möglichkeiten für die Optimierung.Wir haben auch Undefiniertes Verhalten hier so gut, da das Programm ändern von Variablen (
i
,u
etc..) mehr als einmal zwischen Reihenfolge Punkte. Aus Norm-Entwurf Abschnitt6.5
Absatz 2(Hervorhebung von mir):er zitiert die folgenden code-Beispiele, die als nicht definiert:
In all diesen Beispielen ist der code versucht zu ändern, ein Objekt mehr als einmal in der gleichen Reihenfolge Punkt, die am Ende mit der
;
in jedem dieser Fälle:Unspezifisch Verhalten definiert wird, in der Entwurf der c99-standard in Abschnitt
3.4.4
:und Undefiniertes Verhalten ist definiert in Abschnitt
3.4.3
:und stellt fest, dass:
InformationsquelleAutor Shafik Yaghmour
Meisten Antworten hier zitiert aus C-standard betont, dass das Verhalten dieser Konstrukte nicht definiert sind. Um zu verstehen,, warum das Verhalten dieser Konstrukte nicht definiert sind, lasst uns verstehen, diese Bedingungen zuerst in das Licht der C11-standard:
Sequenziert: (5.1.2.3)
Nicht sequenzielle:
Auswertungen können eine der zwei Dinge:
Reihenfolge Punkt:
Kommen wir nun zu der Frage, für die Ausdrücke wie
standard sagt, dass:
6.5 Ausdrücke:
Daher, den obigen Ausdruck ruft UB da zwei Nebenwirkungen, die auf das gleiche Objekt
i
ist nicht sequenzielle relativ zueinander. Das heißt, es ist nicht sequenziert, ob die Nebenwirkung, die durch Zuordnung zui
wird geschehen, bevor oder nachdem die Nebenwirkung von++
.Je nachdem, ob die Zuweisung erfolgt, bevor oder nachdem das Inkrement, unterschiedliche Ergebnisse produziert werden und das der Fall Undefiniertes Verhalten.
Ermöglicht das umbenennen des
i
auf der linken Seite der Zuweisung werdenil
und an der rechten Seite der Zuweisung (im Ausdrucki++
)ir
, dann wird der Ausdruck wieEin wichtiger Punkt bezüglich Postfix
++
operator ist, dass:Bedeutet der Ausdruck
il = ir++
beurteilt werden konnten, entweder alsoder
so dass zwei unterschiedliche Ergebnisse
1
und2
die abhängig von der Reihenfolge der Nebenwirkungen durch Abtretung und++
und daher ruft UB.InformationsquelleAutor haccks
Anderen Weg, dies zu beantworten, anstatt Sie zu verzetteln in arcane details der Sequenz-Punkte und Undefiniertes Verhalten, ist, Sie einfach zu Fragen, was sollen Sie bedeuten? Was war der Programmierer, der versucht zu tun?
Das erste fragment gefragt,
i = i++ + ++i
ist ziemlich eindeutig verrückt in meinem Buch. Niemand würde jemals einen schreiben es in ein richtiges Programm, es ist nicht offensichtlich, was es tut, es gibt keinen denkbaren Algorithmus, jemand könnte versucht haben, um code, der zur Folge hätte, die in dieser besonderen erfundenen Reihenfolge der Operationen. Und da es nicht offensichtlich ist dir und mir, was es tun soll, es ist in Ordnung in meinem Buch, wenn der compiler nicht herausfinden können, was es tun soll, entweder.Dem zweiten fragment
i = i++
, ist ein wenig leichter zu verstehen. Jemand versucht offensichtlich erhöhen ich, und weisen das Ergebnis wieder zu ich. Aber es gibt ein paar Möglichkeiten, dies zu tun in C. Die einfachste Möglichkeit, hinzufügen, 1 bis i, und weisen das Ergebnis wieder ich, ist das gleiche in fast jeder Programmiersprache:C, natürlich, hat eine praktische Abkürzung:
Bedeutet dies, "hinzufügen von 1 bis i, und weisen das Ergebnis wieder nach i". Also, wenn wir bauen ein Mischmasch von beiden, durch das schreiben
was wir wirklich sagen, ist "hinzufügen, 1 bis i, und weisen das Ergebnis wieder ich, und weisen das Ergebnis wieder nach i". Wir sind verwirrt, so dass es nicht stört mich zu viel, wenn der compiler Durcheinander kommt, auch.
Realistisch gesehen, das einzige mal, diese verrückte Ausdrücke, geschrieben, ist wenn die Leute Sie als künstliche Beispiele, wie ++ funktionieren soll. Und natürlich ist es wichtig zu verstehen, wie ++ arbeitet. Aber eine praktische Regel für die Verwendung von ++ ist, "Wenn es nicht offensichtlich, was ein Ausdruck mit ++ bedeutet, dass Sie nicht schreiben Sie es."
Wir verbrachten unzählige Stunden auf comp.lang.c Diskussion Ausdrücke wie diese und warum Sie sind undefiniert. Zwei meiner längeren Antworten, die versuchen zu erklären, warum sind im Internet archiviert:
*p=(*q)++;
zu bedeutenif (p!=q) *p=(*q)++; else *p= __ARBITRARY_VALUE;
ist das nicht mehr der Fall. Hyper-moderne C erfordern würde, etwas zu schreiben wie die letztere Formulierung (aber es gibt keine standardisierte Möglichkeit, den Anzeige-code egal ist, was in*p
) zu erreichen das Niveau der Effizienz-Compiler verwendet, um mit der alten (dieelse
- Klausel ist notwendig, damit der compiler die Optimierung aus derif
welche einige neuere Compiler erfordern würde).Ich habe gesehen, mindestens 5 ähnliche Fragen über diese ++ und -- Wahnsinn, Letzte Woche oder so. Diese scheinen einige Professoren Lieblings-Thema, um Rätsel Ihrer Schüler an.
InformationsquelleAutor Steve Summit
Oft ist diese Frage verbunden ist, als ein Duplikat von Fragen im Zusammenhang mit code wie
oder
oder ähnliche Varianten.
Dies ist zwar auch Undefiniertes Verhalten wie bereits dargelegt, gibt es feine Unterschiede, wenn
printf()
beteiligt ist, wenn im Vergleich zu einer Aussage wie:In der folgenden Anweisung:
den Reihenfolge der Auswertung der Argumente, die in
printf()
ist die nicht spezifiziert. Das bedeutet, dass die Ausdrückei++
und++i
beurteilt werden konnten, in beliebiger Reihenfolge. C11-standard hat einige relevante Beschreibungen auf dieser:Anhang J, unspezifische Verhaltensweisen
3.4.4, nicht spezifiziert Verhalten
Den unspezifisch Verhalten selbst ist NICHT ein Problem. Betrachten Sie dieses Beispiel:
Dieser hat auch unspezifisch Verhalten, weil die Reihenfolge der Auswertung von
++x
undy++
ist nicht spezifiziert. Aber es ist vollkommen legal und gültige Anweisung. Es gibt keine Undefiniertes Verhalten in dieser Aussage. Da die änderungen (++x
undy++
) werden durchgeführt, um zu verschiedene Objekte.Was macht die folgende Anweisung
als Undefiniertes Verhalten ist die Tatsache, dass diese beiden Ausdrücke ändern, die gleichen Objekt
i
ohne zwischengeschalteten sequence point.Ein weiteres detail ist, dass die Komma beteiligten in der printf () - Aufruf ist ein separator, nicht die Komma-operator.
Dies ist eine wichtige Unterscheidung, weil die Komma-operator führt zu einem Reihenfolge Punkt zwischen der Auswertung von Operanden, die die folgenden rechtlichen:
Den Komma-operator, wertet seinen Operanden von Links nach rechts und liefert nur den Wert des letzten Operanden. So in
j = (++i, i++);
,++i
Schritteni
zu6
undi++
Erträge alten Wert voni
(6
) zugeordnet istj
. Danni
wird7
durch post-Inkrement.Also, wenn die Komma in dem Aufruf der Funktion ein Komma-operator dann
wird kein problem sein. Aber es ruft Undefiniertes Verhalten, weil die Komma hier ist ein separator.
Für diejenigen, die neue Undefiniertes Verhalten würde davon profitieren, Lesen Das, Was Jeder C-Programmierer Sollte Wissen, Über Undefiniertes Verhalten zu verstehen, das Konzept und viele andere Varianten von Undefiniertes Verhalten in C
Diesem post: Undefinierten, unbestimmten und Implementierung-definiert das Verhalten ist auch relevant.
int a = 10, b = 20, c = 30; printf("a=%d b=%d c=%d\n", (a = a + b + c), (b = b + b), (c = c + c));
erscheint zu stabilen Verhalten (rechts-nach-Links-argument evaluation des gcc v7.3.0; Ergebnis "a=110 b=40 c=60"). Ist es, weil die Aufgaben werden als "full-Aussagen" und damit die Einführung einer Sequenz zeigen? Sollte nicht das Ergebnis in der linken-zu-Recht-argument/Aussage der Auswertung? Oder ist es einfach nur äußerung von undefiniertem Verhalten?Das printf-Anweisung beinhaltet Undefiniertes Verhalten, aus dem gleichen Grund oben erklärt. Sie schreiben
b
undc
in 3rd & 4. Argumente bzw. und Lesen im 2. argument. Aber es gibt keine Reihenfolge zwischen diesen Ausdrücken (2nd, 3rd & 4th args). gcc/clang hat eine option-Wsequence-point
die helfen können, finden diese auch.InformationsquelleAutor P.P.
Während es ist unwahrscheinlich, dass irgendwelche Compiler und Prozessoren würde tatsächlich tun Sie so, es legal sein würde, unter den C-standard, für den compiler zu implementieren "i++" mit der Folge:
Während ich glaube nicht, dass alle Prozessoren, die Unterstützung der hardware zu erlauben, solch ein Ding sein getan effizient, man kann sich leicht Situationen vorstellen, wo ein solches Verhalten würde die multi-threaded-code zu erleichtern (z.B. es würde garantieren, dass, wenn zwei threads versuchen, zur Durchführung der oben genannten Reihenfolge gleichzeitig
i
bekommen würde erhöht durch zwei) und es ist nicht völlig undenkbar, dass irgendwann in der Zukunft-Prozessor könnte eine Funktion so ähnlich.Wenn der compiler zu schreiben
i++
wie oben angegeben (legal unter der Norm) und waren, um durchzusetzen die oben genannten Anweisungen während der Auswertung des gesamten Ausdrucks (auch legal), und wenn das nicht der Fall zu bemerken, dass eine der anderen Anweisungen geschehen, um den Zugangi
wäre es möglich (und legal) für den compiler generiert eine Sequenz von Anweisungen, die würde deadlock. Um sicher zu sein, ein compiler würde mit ziemlicher Sicherheit erkennen, die das problem in dem Fall, wo die gleiche variablei
wird in beide Orte, aber wenn eine routine akzeptiert Verweise auf zwei Zeigerp
undq
und verwendet(*p)
und(*q)
im obigen Ausdruck (anstatti
zweimal) würde der compiler nicht verpflichtet, zu erkennen oder vermeiden von deadlock, die auftreten würde, wenn das gleiche Objekt Adresse übergeben wurden, für beidep
undq
.InformationsquelleAutor supercat
C-standard sagt, dass eine variable sollte nur zugewiesen werden, höchstens einmal zwischen zwei sequenzpunkten. Ein semi-colon zum Beispiel ist eine Sequenz zeigen.
So ist jede Aussage der form:
und so weiter verletzen diese Regel. Die Norm sagt auch, dass das Verhalten undefiniert ist und nicht unspezifisch. Einige Compiler erkennen diese und produzieren ein Ergebnis, aber dieses ist nicht per standard.
Jedoch zwei verschiedene Variablen können inkrementiert werden zwischen zwei sequenzpunkten.
Oben ist eine gemeinsame Codierung der Praxis beim kopieren/Analyse von strings.
InformationsquelleAutor Nikhil Vidhani
Während der syntax der Ausdrücke wie
a = a++
odera++ + a++
legal ist, die Verhalten dieser Konstrukte ist undefined weil ein wird im C-standard nicht eingehalten. C99-6.5p2:Mit Fußnote 73 die weitere Klarstellung, dass
Den verschiedenen Sequenz-Punkte sind in Anhang C aufgeführt von C11 (und C99):
Dem Wortlaut der gleichen Absatz in C11 ist:
Können Sie erkennen, wie Fehler in einem Programm zum Beispiel durch Verwendung einer aktuellen version von GCC mit
-Wall
und-Werror
, und dann wird GCC geradezu verweigern, kompilieren Sie Ihr Programm. Das folgende ist die Ausgabe von gcc (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005:Ist der wichtige Teil ist, zu wissen,was für ein sequence point ist -- und was eine Sequenz Punkt, und was die nicht. Zum Beispiel die Komma-operator ist ein sequence point, so
ist gut definiert, und erhöht
i
von einer, das nachgeben der alte Wert, zu verwerfen, Wert; dann bei Komma-operator, siedeln sich die Nebenwirkungen, und dann erhöhti
von einem, und der resultierende Wert wird der Wert des Ausdrucks - D. H. dies ist nur eine erfundene Art zu schreibenj = (i += 2)
die ist doch nochmal eine "clevere" Art zu schreibenJedoch die
,
in Funktion-argument-Listen ist nicht eine Komma-operator, und es gibt keine Reihenfolge Punkt zwischen den Bewertungen der unterschiedlichen Argumente; stattdessen werden deren Bewertungen sind nicht sequenzielle Bezug zu einander, so der Aufruf der Funktionhat Undefiniertes Verhalten weil es gibt keine Reihenfolge Punkt zwischen den Bewertungen der
i++
und++i
in der Funktion Argumente, und der Wert voni
ist daher zweimal geändert, indem beidei++
und++i
zwischen dem vorherigen und dem nächsten sequence point.InformationsquelleAutor Antti Haapala
In https://stackoverflow.com/questions/29505280/incrementing-array-index-in-c jemand fragte über eine Aussage wie:
dem Drucke 7... der OP erwartet zum drucken 6.
Den
++i
Schritten nicht garantiert, dass alle abgeschlossen sein, bevor der rest der Berechnungen. In der Tat, verschiedene Compiler unterschiedliche Ergebnisse erhalten Sie hier. In dem Beispiel, das Sie zur Verfügung gestellt, die ersten 2++i
ausgeführt, dann werden die Werte desk[]
gelesen wurden, dann ist die Letzte++i
dannk[]
.Moderne Compiler optimieren dies sehr gut. In der Tat, möglicherweise besser als der code, den Sie ursprünglich geschrieben haben (vorausgesetzt, es hatte geklappt wie Sie es erhofft hatten).
InformationsquelleAutor TomOnTime
Ihre Frage war wohl nicht, "Warum sind diese Konstrukte Undefiniertes Verhalten in C?". Ihre Frage war wohl, Warum dieser code (mit
++
) mir nicht den Wert, den ich erwarten?", und jemand markiert Ihre Frage als Duplikat, und dich hierher geschickt.Diese beantworten versucht, diese Frage zu beantworten: warum ist dein code nicht geben Ihnen die Antwort, die Sie erwartet, und wie können Sie lernen, zu erkennen (und vermeiden) Ausdrücke, die nicht so funktionieren wie erwartet.
Ich nehme an, Sie haben gehört, dass Sie die grundlegende definition von C
++
und--
Betreiber von jetzt, und wie die Präfix-form++x
unterscheidet sich von der postfix-formx++
. Aber diese Operatoren sind hart zu denken, um sicherzustellen, dass Sie verstanden, vielleicht schrieb Sie einen kleinen test-Programm, an dem so etwas wieAber, zu Ihrer überraschung, dieses Programm hat nicht helfen, Sie zu verstehen -- es gedruckt, einige seltsame, unerwartete, unerklärliche Ausgabe, was darauf hindeutet, dass vielleicht
++
tut etwas ganz anderes, überhaupt nicht, was Sie dachte, es Tat.Oder vielleicht suchen Sie in einem schwer zu verstehenden Ausdruck wie
Vielleicht hat jemand Ihnen, dass code wie ein puzzle. Dieser code macht auch keinen Sinn, vor allem wenn man es-und wenn Sie kompilieren und führen Sie es unter zwei verschiedenen Compilern, die Sie wahrscheinlich zu bekommen, zwei verschiedene Antworten! Was es damit auf sich? Welche Antwort ist richtig? (Und die Antwort ist, dass beide, oder keiner von Ihnen sind).
Da hast du jetzt gehört, alle diese Ausdrücke sind undefined, was bedeutet, dass die Sprache C garantiert nicht über das, was Sie tun werden. Dies ist eine seltsame und überraschende Ergebnis, weil Sie wohl dachte, dass jedes Programm, das Sie schreiben konnte, solange es kompiliert und lief, würde generieren eines eindeutigen, klar definierten Ausgang. Aber im Fall von undefinierten Verhalten, das ist nicht so.
Was macht ein Ausdruck undefiniert? Sind Ausdrücke mit
++
und--
immer undefiniert? Natürlich nicht: diese sind nützlich, Operatoren und, wenn Sie Sie richtig zu verwenden, Sie sind sehr gut definiert.Für die Ausdrücke, reden wir über das, was macht Sie undefiniert ist, wenn es zu viel Los auf einmal, wenn wir nicht sicher sind, in welcher Reihenfolge die Dinge passieren, aber wenn die Bestellung ankommt, um das Ergebnis, das wir bekommen.
Gehen wir zurück zu den beiden Beispielen, die ich verwendet habe, in diese Antwort. Wenn ich schrieb
die Frage ist, vor dem Aufruf
printf
, weiß der compiler berechnen Sie den Wert vonx
ersten, oderx++
oder vielleicht++x
? Aber es stellt sich heraus wir wissen nicht. Es gibt keine Regel in C, die sagt, dass die Argumente einer Funktion ausgewertet Links-nach-rechts oder von-rechts-nach-Links oder in anderer Reihenfolge. Wir können also nicht sagen, ob der compilerx
zuerst, dann++x
, dannx++
oderx++
dann++x
dannx
oder eine andere Reihenfolge. Aber die, um die Dinge eindeutig, denn je nachdem, in welcher Reihenfolge der compiler verwendet, werden wir deutlich unterschiedliche Ergebnisse erhalten gedruckt vonprintf
.Was ist mit diesem verrückten Ausdruck?
Das problem mit diesem Ausdruck ist, dass es enthält drei verschiedene versuche, ändern Sie den Wert von x: (1) die
x++
Teil versucht, hinzufügen von 1 bis x, der neue Wert gespeichert inx
, und geben den alten Wert vonx
; (2) die++x
Teil versucht, hinzufügen von 1 bis x, der neue Wert gespeichert inx
, und der neue Wert wird zurückgegeben vonx
; und (3) diex =
Teil versucht, weisen Sie die Summe der anderen beiden zurück zu x ist. Welche dieser drei versuchten Zuordnungen wird "gewinnen"? Welche der drei Werte tatsächlich zugewiesen bekommenx
? Wieder, und vielleicht überraschend, es gibt keine Regel in C zu erzählen.Könnten Sie sich vorstellen, dass Vorrang-oder Assoziativität oder Links-nach-rechts-Auswertung sagt Ihnen, in welcher Reihenfolge die Dinge passieren, aber Sie tun es nicht. Sie können mir nicht glauben, aber bitte nehmen Sie mein Wort für Sie, und ich werde es wieder sagen: Priorität und Assoziativität der Operatoren bestimmen nicht jeden Aspekt der evaluation, um eine expression in C. insbesondere, wenn innerhalb eines Ausdrucks gibt es mehrere verschiedene spots, wo wir versuchen, einen neuen Wert zuweisen, um so etwas wie
x
Vorrang und Assoziativität tun nicht uns sagen, welche solche versuche, passiert erste, oder Letzte, oder etwas.So, mit all diesem hintergrund und der Einführung aus dem Weg, wenn Sie wollen, stellen Sie sicher, dass alle Ihre Programme sind gut definiert, die Ausdrücken können, die Sie schreiben, und welche können Sie nicht schreiben?
Diese Ausdrücke sind alle in Ordnung:
Diese Ausdrücke sind alle undefined:
Und die Letzte Frage ist, wie können Sie sagen, welche Ausdrücke sind klar definiert, und die Ausdrücke sind nicht definiert?
Wie ich bereits sagte, die unbestimmten Ausdrücke sind diejenigen, bei denen es passiert so viel auf einmal, wo Sie können nicht sicher sein, in welcher Reihenfolge die Dinge passieren und in dem die Reihenfolge ist wichtig:
Als ein Beispiel #1, in der die expression
gibt es drei versuche, ändern Sie `x.
Als ein Beispiel #2 in dem Ausdruck
wir beide benutzen den Wert von
x
, und ändern Sie es.So, dass ist die Antwort: stellen Sie sicher, dass in jedem Ausdruck, den Sie schreiben, jede variable wird geändert, höchstens einmal, und wenn eine variable geändert wird, wird Sie nicht auch versuchen, mit dem Wert der variable irgendwo anders.
InformationsquelleAutor Steve Summit
Eine gute Erklärung über das, was passiert in dieser Art von Berechnung wird im Dokument n1188 von die ISO-W14-Website.
Ich erkläre die Ideen.
Die wichtigste Regel aus dem standard ISO 9899, gilt in diesem Fall ist 6.5p2.
Der Reihenfolge Punkte in einem Ausdruck wie
i=i++
sind vori=
und nachi++
.In der Zeitung, den ich oben zitiert, es wird erklärt, dass Sie herausfinden können, das Programm so ausgebildet ist, von kleinen Boxen, jede box enthält die Anweisungen, die zwischen 2 aufeinanderfolgenden Sequenz Punkte. Die Reihenfolge der Punkte definiert, die in Anhang C der Norm, im Fall der
i=i++
es stehen 2 Sequenz-Punkte, die trennen eine full-Ausdruck. Ein solcher Ausdruck ist syntaktisch gleichwertig mit einem Eintrag vonexpression-statement
in der Backus-Naur-form der Grammatik (Grammatik ist im Anhang A der Norm).Also die Reihenfolge der Anweisungen innerhalb eines Kastens hat keine klare Reihenfolge.
interpretiert werden kann, wie
oder als
weil beide alle diese Formen zu interpretieren, die code
i=i++
gültig sind und weil beide erzeugen unterschiedliche Antworten, ist das Verhalten undefiniert.Also ein Sequenz-Punkt kann gesehen werden, indem der Anfang und das Ende jeder box, komponiert das Programm [die Kisten sind Atomare Einheiten C] in einem box-die Reihenfolge der Anweisungen ist nicht definiert in allen Fällen. Änderung dieser Reihenfolge kann man ändern das Ergebnis manchmal.
EDIT:
Andere gute Quelle für die Erklärung solcher Unklarheiten sind die Einträge aus c-faq Website (auch veröffentlicht als Buch) , nämlich hier und hier und hier .
i=i++
ist sehr ähnlich zu Antwort.Ich habe nicht die anderen Antworten. Ich wollte erklären, in meiner eigenen Sprache, was ich gelernt habe aus dem genannten Dokument von der offiziellen Website der ISO 9899 open-std.org/jtc1/sc22/wg14/www/docs/n1188.pdf
InformationsquelleAutor alinsoar
Der Grund dafür ist, dass das Programm läuft nicht definiertes Verhalten. Das problem liegt in der Bewertung um, da gibt es keine Reihenfolge die erforderlichen Punkte nach C++98-standard ( keine Operationen sequenziert vor oder nach der anderen nach C++11-Terminologie).
Jedoch, wenn Sie an einen compiler, finden Sie das Verhalten persistent, solange Sie nicht die Funktion " add " Aufrufe oder Hinweise, die das Verhalten mehr chaotisch.
Also erstmal den GCC:
Mit Nuwen MinGW 15 GCC 7.1 erhalten Sie:
}
Wie funktioniert GCC arbeiten? es wertet sub-Ausdrücke in einer von Links nach rechts für die Rechte Seite (RHS) , dann wird der Wert auf der linken Seite (LHS) . Das ist genau wie Java und C# - Verhalten und definieren Ihre standards. (Ja, die äquivalente software in Java und C# definiert hat, "Verhalten"). Es wertet jede sub-Ausdruck eins zu eins in der RS-Anweisung, die in einer von Links nach rechts; für jede sub-Ausdruck: die c ++(pre-increment) wird zuerst ausgewertet, dann wird der Wert c ist für den Betrieb, dann ist der post-Inkrement c++).
laut GCC C++: Betreiber
den entsprechenden code in definierten Verhalten C++ GCC versteht:
Dann gehen wir zu Visual Studio. Visual Studio 2015, erhalten Sie:
Wie funktioniert visual studio arbeiten, braucht es einen anderen Ansatz, es wertet alle pre-Schritten Ausdrücke im ersten Durchgang, dann werden Variablen-Werte in die Vorgänge im zweiten Durchgang, zuweisen von RHS, LHS im Dritten Durchgang, dann, endlich, pass es wertet alle post-Inkrement-Ausdrücke in einem Durchgang.
Also den Gegenwert in einer definierten Verhalten C++ als Visual C++ versteht:
als Visual Studio-Dokumentation-Staaten an Vorrang und Reihenfolge der Bewertung:
Auch die Frage über das c, nun, nicht C++
InformationsquelleAutor Mohamed El-Nakeep