Warum ist x = x ++ undefiniert?

19

Es ist undefiniert, da es xzwischen Sequenzpunkten zweimal geändert wird. Der Standard sagt, es ist undefiniert, daher ist es undefiniert.
Soviel weiß ich.

Aber wieso?

Meines Wissens nach können Compiler besser optimieren, wenn dies untersagt wird. Dies hätte Sinn machen können, als C erfunden wurde, aber jetzt scheint es ein schwaches Argument zu sein.
Wenn wir C heute neu erfinden würden, würden wir es dann so machen, oder könnte es besser gemacht werden?
Oder gibt es ein tieferes Problem, das es schwierig macht, konsistente Regeln für solche Ausdrücke zu definieren, also ist es am besten, sie zu verbieten?

Nehmen wir also an, wir würden C heute neu erfinden. Ich möchte einfache Regeln für Ausdrücke wie vorschlagen x=x++, die meiner Meinung nach besser funktionieren als die vorhandenen Regeln.
Ich würde gerne Ihre Meinung zu den vorgeschlagenen Regeln im Vergleich zu den vorhandenen oder zu anderen Vorschlägen einholen.

Vorgeschlagene Regeln:

  1. Zwischen den Sequenzpunkten ist die Reihenfolge der Auswertung nicht angegeben.
  2. Nebenwirkungen treten sofort auf.

Es ist kein undefiniertes Verhalten beteiligt. Ausdrücke werden auf diesen oder jenen Wert ausgewertet, formatieren Ihre Festplatte jedoch nicht (seltsamerweise habe ich noch nie eine Implementierung gesehen, bei der x=x++die Festplatte formatiert wird).

Beispielausdrücke

  1. x=x++- Gut definiert, ändert sich nicht x.
    Zuerst xwird inkrementiert (sofort, wenn x++ausgewertet wird), dann wird der alte Wert in gespeichert x.

  2. x++ + ++x- Inkrementiert xzweimal, ergibt 2*x+2.
    Obwohl jede Seite zuerst bewertet werden kann, ist das Ergebnis entweder x + (x+2)(linke Seite zuerst) oder (x+1) + (x+1)(rechte Seite zuerst).

  3. x = x + (x=3)- Nicht angegeben, xauf entweder x+3oder eingestellt 6.
    Wenn die rechte Seite zuerst ausgewertet wird, ist es x+3. Es ist auch möglich, dass x=3zuerst ausgewertet wird, also ist es 3+3. In beiden Fällen erfolgt die x=3Zuweisung sofort x=3nach der Auswertung, sodass der gespeicherte Wert durch die andere Zuweisung überschrieben wird.

  4. x+=(x=3)- Gut definiert, setzt xauf 6.
    Sie könnten argumentieren, dass dies nur eine Abkürzung für den obigen Ausdruck ist.
    Aber ich würde sagen, das +=muss nachher ausgeführt x=3werden und nicht in zwei Teilen (lesen x, auswerten x=3, addieren und neuen Wert speichern).

Was ist der vorteil

Einige Kommentare haben diesen guten Punkt angesprochen.
Ich denke nicht, dass Ausdrücke, wie x=x++sie in einem normalen Code verwendet werden sollten.
Eigentlich bin ich viel strenger als das - ich halte die einzig gute Verwendung für rein x++wie x++;alleine.

Ich denke jedoch, dass die Sprachregeln so einfach wie möglich sein müssen. Sonst verstehen sie die Programmierer einfach nicht. Die Regel, die das zweimalige Ändern einer Variablen zwischen Sequenzpunkten verbietet, ist sicherlich eine Regel, die die meisten Programmierer nicht verstehen.

Eine sehr grundlegende Regel lautet:
Wenn A gültig ist und B gültig ist und sie auf gültige Weise kombiniert werden, ist das Ergebnis gültig.
xist ein gültiger L-Wert, x++ein gültiger Ausdruck und =eine gültige Möglichkeit, einen L-Wert und einen Ausdruck zu kombinieren. Warum x=x++ist das nicht zulässig?
Der C-Standard macht hier eine Ausnahme, und diese Ausnahme kompliziert die Regeln. Sie können stackoverflow.com durchsuchen und sehen, wie sehr diese Ausnahme die Menschen verwirrt.
Also sage ich - befreie dich von dieser Verwirrung.

=== Zusammenfassung der Antworten ===

  1. Warum das tun?
    Ich habe versucht, dies im obigen Abschnitt zu erklären - ich möchte, dass C-Regeln einfach sind.

  2. Optimierungspotential:
    Dies nimmt dem Compiler zwar etwas Freiheit, aber ich habe nichts gesehen, was mich davon überzeugt hätte, dass es von Bedeutung sein könnte.
    Die meisten Optimierungen können noch durchgeführt werden. Beispielsweise a=3;b=5;kann nachbestellt werden, obwohl der Standard die Reihenfolge festlegt. Ausdrücke wie a=b[i++]können noch ähnlich optimiert werden.

  3. Sie können den vorhandenen Standard nicht ändern.
    Ich gebe zu, ich kann nicht. Ich hätte nie gedacht, dass ich tatsächlich Standards und Compiler ändern kann. Ich wollte nur darüber nachdenken, ob die Dinge anders hätten gemacht werden können.

ugoren
quelle
10
Warum ist dir das wichtig? Sollte es definiert werden und wenn ja, warum? Es hat nicht viel Sinn, xsich selbst zuzuweisen , und wenn Sie inkrementieren möchten, xkönnen Sie einfach sagen x++;- die Zuweisung ist nicht erforderlich. Ich würde sagen, es sollte nicht definiert werden, nur weil es schwierig ist, sich daran zu erinnern, was passieren soll.
Caleb
4
In meinen Augen ist dies eine gute Frage ("Manche Männer sehen die Dinge so wie sie sind und fragen warum, ich träume Dinge, die es nie gab und frage warum nicht"). Es ist (meiner Meinung nach) eine reine Frage zum Sprachdesign, wobei C-Syntax als Beispiel dient und keine Frage zur C-Syntax. Ich persönlich denke, dass wir das Verhalten für Ausdrücke wie x ++ + ++ x oder x = x ++ nicht definiert haben, weil die Möglichkeit besteht, dass sie falsch verstanden werden.
Jamie Taylor
5
@ugoren: Warum musst du das Ergebnis vorhersagen? Niemand, der bei Verstand ist, würde einen solchen Code schreiben (wie bereits mehrfach erwähnt), selbst wenn Sie einen solchen Code schreiben würden, würde dieser bei der ersten Codeüberprüfung abgelehnt. Es ist also nicht erforderlich, das Verhalten zu definieren und dem Optimierer die beste Chance zu geben, es zu optimieren. In jedem Beispiel, das Sie vorschlagen, würde ich jemanden erschießen, wenn er das zur Codebasis hinzufüge.
Martin York
3
Ich würde eine interessantere Frage finden, warum es kein Fehler ist, dies zu schreiben? Sicherlich könnte ein Compiler ein undefiniertes Verhalten erkennen und daher möglicherweise nicht das sein, was der Benutzer eigentlich wollte. Warum also liegt kein Fehler vor? Ich verstehe, dass einige Fälle von undefiniertem Verhalten schwer zu erkennen sind, aber dies ist nicht der Fall
JohnB
3
" Die Regel, die das zweimalige Ändern einer Variablen zwischen Sequenzpunkten verbietet, ist sicherlich eine Regel, die die meisten Programmierer nicht verstehen. " - Haben Sie Beweise dafür? Die meisten Fragen, die ich gesehen habe, wurden gestellt, weil die Programmierer nicht über die Regel Bescheid wussten . Gibt es Hinweise darauf, dass die meisten von ihnen es nach der Erklärung immer noch nicht verstanden haben?
Sichern Sie sich den

Antworten:

24

Vielleicht solltest du zuerst die Frage beantworten, warum es definiert werden soll? Gibt es einen Vorteil in Bezug auf Programmierstil, Lesbarkeit, Wartbarkeit oder Leistung, wenn solche Ausdrücke mit zusätzlichen Nebenwirkungen kombiniert werden? Ist

y = x++ + ++x;

lesbarer als

y = 2*x + 2;
x += 2;

Angesichts dessen, dass eine solche Änderung äußerst grundlegend ist und die bestehende Codebasis verletzt.

Sichern
quelle
1
Ich habe meiner Frage einen "Warum" -Abschnitt hinzugefügt. Ich schlage sicherlich nicht vor, diese Ausdrücke zu verwenden, aber ich bin daran interessiert, einfache Regeln zu haben, um die Bedeutung eines Ausdrucks zu erkennen.
Ugoren
Außerdem wird durch diese Änderung kein vorhandener Code beschädigt, es sei denn, es wurde ein undefiniertes Verhalten ausgelöst. Korrigiere mich, wenn ich falsch liege.
Ugoren
3
Nun, eine philosophischere Antwort: Es ist derzeit undefiniert. Wenn es kein Programmierer verwendet, müssen Sie solche Ausdrücke nicht verstehen, da es keinen Code geben sollte. Wenn Sie sie verstehen müssen, muss es offensichtlich eine Menge Code geben, der auf undefiniertem Verhalten beruht. ;)
Sichern Sie sich den
1
Per Definition wird keine vorhandene Codebasis gebrochen, um das Verhalten zu definieren. Wenn sie UB enthielten, waren sie per Definition bereits defekt.
DeadMG
1
@ugoren: Ihr "Warum" -Abschnitt beantwortet immer noch nicht die praktische Frage: Warum möchten Sie diesen verrückten Ausdruck in Ihrem Code? Wenn Sie keine überzeugende Antwort darauf finden können, ist die ganze Diskussion umstritten.
Mike Baranczak
20

Das Argument, dass dieses undefinierte Verhalten eine bessere Optimierung ermöglicht, ist heute nicht schwach. Tatsächlich ist es heute viel stärker als damals, als C neu war.

Als C neu war, waren Maschinen, die dies für eine bessere Optimierung nutzen konnten, meist theoretische Modelle. Es wurde über die Möglichkeit gesprochen, CPUs zu bauen, bei denen der Compiler die CPU anweist, welche Anweisungen parallel zu anderen Anweisungen ausgeführt werden können / sollen. Sie wiesen auf die Tatsache hin, dass das Zulassen eines undefinierten Verhaltens dazu führte, dass Sie auf einer solchen CPU, falls es jemals wirklich existierte, den "Inkrement" -Teil des Befehls so einplanen konnten, dass er parallel zum Rest des Befehlsstroms ausgeführt wird. Während sie in Bezug auf die Theorie recht hatten, gab es zu der Zeit wenig Hardware, die diese Möglichkeit wirklich ausnutzen konnte.

Das ist nicht mehr nur theoretisch. Jetzt gibt es Hardware in der Produktion und in großem Umfang im Einsatz (z. B. Itanium-, VLIW-DSPs), die diese Vorteile wirklich nutzen können. Sie wirklich tun , kann der Compiler einen Befehlsstrom zu erzeugen, der angibt , daß die Befehle X, Y und Z können alle parallel ausgeführt werden. Dies ist kein theoretisches Modell mehr - es ist echte Hardware, die im realen Einsatz echte Arbeit leistet.

IMO, dieses definierte Verhalten zu erzeugen, ist nahezu die schlechteste "Lösung" für das Problem. Sie sollten solche Ausdrücke eindeutig nicht verwenden. Für die überwiegende Mehrheit des Codes wäre es das ideale Verhalten, wenn der Compiler solche Ausdrücke einfach vollständig ablehnt. Zu dieser Zeit haben C-Compiler nicht die Flussanalyse durchgeführt, die erforderlich war, um dies zuverlässig zu erkennen. Selbst zum Zeitpunkt der ursprünglichen C-Norm war dies noch gar nicht üblich.

Ich bin mir auch heute nicht sicher, ob dies für die Community akzeptabel ist - während viele Compiler diese Art der Flussanalyse durchführen können, tun sie dies in der Regel nur, wenn Sie eine Optimierung anfordern. Ich bezweifle, dass die meisten Programmierer die Idee, "Debug" -Builds zu verlangsamen, nur um Code ablehnen zu können, den sie (weil sie vernünftig sind) niemals schreiben würden.

Was C getan hat, ist eine halbwegs vernünftige zweitbeste Wahl: Sagen Sie den Leuten, dass sie das nicht tun sollen, und erlauben Sie dem Compiler, den Code abzulehnen (aber nicht zu verlangen). Dies verhindert (noch weiter), dass die Kompilierung für Benutzer verlangsamt wird, die sie nie verwenden würden, ermöglicht es jedoch jemandem, einen Compiler zu schreiben, der solchen Code ablehnt, wenn er möchte (und / oder Flags hat, die ihn ablehnen, die Benutzer verwenden können) oder nicht, wie sie es für richtig halten).

Zumindest bei IMO wäre es (zumindest in der Nähe) die schlechteste Entscheidung , dieses definierte Verhalten zu treffen. Bei Hardware im VLIW-Stil sollten Sie langsameren Code für die sinnvolle Verwendung der Inkrement-Operatoren generieren, nur um sie zu missbrauchen, oder immer eine ausführliche Ablaufanalyse durchführen, um zu beweisen, dass Sie sich nicht damit befassen beschissener Code, so dass Sie den langsamen (serialisierten) Code nur dann produzieren können, wenn es wirklich notwendig ist.

Fazit: Wenn Sie dieses Problem beheben möchten, sollten Sie in die entgegengesetzte Richtung denken. Anstatt zu definieren, was ein solcher Code tut, sollten Sie die Sprache definieren, damit solche Ausdrücke überhaupt nicht erlaubt sind (und mit der Tatsache leben, dass sich die meisten Programmierer wahrscheinlich für eine schnellere Kompilierung entscheiden, anstatt diese Anforderung durchzusetzen).

Jerry Sarg
quelle
IMO, es gibt wenig Grund zu der Annahme, dass die langsameren Anweisungen in den meisten Fällen wirklich so viel langsamer sind als die schnellen Anweisungen und dass diese sich immer auf die Programmleistung auswirken. Ich würde dieses unter vorzeitiger Optimierung klassifizieren.
DeadMG
Vielleicht fehlt mir etwas - wenn niemand jemals einen solchen Code schreiben sollte, warum sollte er dann optimiert werden?
Ugoren
1
@ugoren: Das Schreiben von Code wie a=b[i++];(zum Beispiel) ist in Ordnung und das Optimieren ist eine gute Sache. Ich sehe jedoch keinen Sinn darin, vernünftigen Code so zu verletzen, nur damit so etwas ++i++eine definierte Bedeutung hat.
Jerry Coffin
2
@ugoren Das Problem liegt in der Diagnose. Der einzige Zweck, solche Ausdrücke nicht direkt abzulehnen, ++i++ist genau, dass es im Allgemeinen schwierig ist, sie von gültigen Ausdrücken mit Nebenwirkungen (wie a=b[i++]) zu unterscheiden. Es mag für uns einfach genug erscheinen, aber wenn ich mich richtig an das Drachenbuch erinnere , ist es tatsächlich ein NP-hartes Problem. Aus diesem Grund ist dieses Verhalten nicht verboten, sondern UB.
Konrad Rudolph
1
Ich glaube nicht, dass Leistung ein gültiges Argument ist. Ich kämpfe darum zu glauben, dass der Fall angesichts des sehr geringen Unterschieds und der sehr schnellen Ausführung in beiden Fällen häufig genug ist, damit sich ein kleiner Leistungseinbruch bemerkbar macht - ganz zu schweigen davon, dass es auf vielen Prozessoren und Architekturen praktisch kostenlos ist, ihn zu definieren.
DeadMG
9

Eric Lippert, Chefdesigner im C # -Compiler-Team, veröffentlichte in seinem Blog einen Artikel über eine Reihe von Überlegungen, die dazu führen, dass ein Feature auf der Ebene der Sprachspezifikationen nicht definiert wird. Offensichtlich ist C # eine andere Sprache, wobei verschiedene Faktoren in das Sprachdesign einfließen, aber die Punkte, die er hervorhebt, sind dennoch relevant.

Insbesondere weist er auf die Frage hin, ob es Compiler für eine Sprache gibt, die bereits implementiert sind und Vertreter in einem Ausschuss haben. Ich bin mir nicht sicher, ob dies der Fall ist, bin mir aber für die meisten C- und C ++ -bezogenen Spezifikationsdiskussionen relevant.

Bemerkenswert ist auch, wie Sie sagten, das Leistungspotential für die Compileroptimierung. Zwar ist die Leistung von CPUs heutzutage um ein Vielfaches höher als zu Zeiten, als C noch jung war, doch wird ein Großteil der C-Programmierung heutzutage aufgrund des möglichen Leistungsgewinns und des Potenzials für eine (hypothetische) Zukunft durchgeführt ) CPU-Befehlsoptimierungen und Multicore-Verarbeitungsoptimierungen wären wegen eines übermäßig restriktiven Regelsatzes für den Umgang mit Nebenwirkungen und Sequenzpunkten dumm auszuschließen.

Tanzelax
quelle
Aus dem Artikel, auf den Sie verlinken, geht hervor, dass C # nicht weit von dem ist, was ich vorschlage. Die Reihenfolge der Nebenwirkungen ist definiert als "wenn sie von dem Thread aus beobachtet werden, der die Nebenwirkungen verursacht". Ich habe Multithreading nicht erwähnt, aber im Allgemeinen garantiert C einem Beobachter in einem anderen Thread nicht viel.
Ugoren
5

Schauen wir uns zunächst die Definition von undefiniertem Verhalten an:

3.4.3

1 undefiniertes Verhalten
Verhalten, bei der Verwendung eines nonportable oder fehlerhaften Programm Konstrukt oder von fehlerhaften Daten, für die diese Internationale Norm keine Anforderungen auferlegt

2 HINWEIS Mögliche undefiniert Verhalten reicht von Ignorieren der Situation vollständig mit unvorhersehbaren Ergebnissen, um während der Übersetzung bzw. den Programmablaufes verhalten in eine dokumentierte Art und Weise, die für die Umgebung charakteristisch ist (mit oder ohne Ausgabe einer Diagnosemeldung), um eine Übersetzung oder Ausführung zu beenden (mit Ausgabe einer Diagnosemeldung).

3 BEISPIEL Ein Beispiel für ein nicht definiertes Verhalten ist das Verhalten bei ganzzahligem Überlauf

Mit anderen Worten, undefiniertes Verhalten bedeutet einfach, dass der Compiler frei ist, die Situation so zu handhaben, wie er möchte, und eine solche Aktion wird als "korrekt" betrachtet.

Die Wurzel des zur Diskussion stehenden Problems ist die folgende Klausel:

6.5 Ausdrücke

...
3 Die Gruppierung von Operatoren und Operanden wird durch die Syntax angegeben. 74) Mit Ausnahme der spezifischen später ed (für den Funktionsaufruf (), &&, ||, ?:, und comma Operatoren), die Reihenfolge der Auswertung der Teilausdrücke und die Reihenfolge , in der Nebenwirkungen stattfinden , sind sowohl unspezifische hrsg .

Betonung hinzugefügt.

Gegeben ein Ausdruck wie

x = a++ * --b / (c + ++d);

die Unterausdrücke a++, --b, c, und ++dkann ausgewertet werden , in beliebiger Reihenfolge . Darüber hinaus können die Nebenwirkungen von a++, --bund ++dvor dem nächsten Sequenzpunkt an einer beliebigen Stelle angewendet werden (IOW, auch wenn a++vor ausgewertet wird --b, ist es nicht garantiert , dass awird aktualisiert , bevor --bausgewertet). Wie andere bereits gesagt haben, ist es der Grund für dieses Verhalten, der Implementierung die Freiheit zu geben, Operationen auf optimale Weise neu zu ordnen.

Aus diesem Grund jedoch Ausdrücke wie

x = x++
y = i++ * i++
a[i] = i++
*p++ = -*p    // this one bit me just yesterday

usw. ergeben unterschiedliche Ergebnisse für unterschiedliche Implementierungen (oder für dieselbe Implementierung mit unterschiedlichen Optimierungseinstellungen oder basierend auf dem umgebenden Code usw.).

Das Verhalten bleibt undefiniert, so dass der Compiler nicht verpflichtet ist, "das Richtige zu tun", wie auch immer dies sein mag. Die oben genannten Fälle sind leicht zu fassen, aber es gibt eine nicht unbedeutende Anzahl von Fällen, die zum Zeitpunkt der Kompilierung schwer bis unmöglich zu fassen wären.

Offensichtlich Sie können eine Sprache gestalten, dass Reihenfolge der Auswertung und der Reihenfolge , in der Nebenwirkungen angewendet werden , sind streng definiert und beide Java und C # tun so, weitgehend die Probleme zu vermeiden , dass die C- und C ++ Definitionen führen.

Warum wurde diese Änderung nach drei Standardrevisionen nicht in C vorgenommen? Zuallererst gibt es C-Code, der 40 Jahre alt ist, und es kann nicht garantiert werden, dass durch eine solche Änderung dieser Code nicht beschädigt wird. Dies stellt eine gewisse Belastung für Compiler-Autoren dar, da durch eine solche Änderung alle vorhandenen Compiler sofort nicht mehr konform wären. Jeder musste bedeutende Änderungen vornehmen. Und selbst auf schnellen, modernen CPUs ist es immer noch möglich, echte Leistungssteigerungen durch Optimierung der Auswertungsreihenfolge zu erzielen.

John Bode
quelle
1
Sehr gute Erklärung des Problems. Ich bin nicht einverstanden, dass ältere Anwendungen beschädigt werden. Die Art und Weise, in der undefiniertes / nicht angegebenes Verhalten implementiert wird, ändert sich manchmal zwischen den Compilerversionen, ohne dass der Standard geändert wird. Ich schlage nicht vor, ein definiertes Verhalten zu ändern.
Ugoren
4

Zuerst muss man verstehen, dass nicht nur x = x ++ undefiniert ist. X = x ++ interessiert niemanden, da es keinen Sinn macht, egal wie Sie es definieren würden. Was undefiniert ist, ist eher wie "a = b ++ wo a und b gleich sind" - dh

void f(int *a, int *b) {
    *a = (*b)++;
}
int i;
f(&i, &i);

Je nachdem, was für die Prozessorarchitektur (und für die umgebenden Anweisungen, falls dies eine komplexere Funktion als das Beispiel ist) am effizientesten ist, kann die Funktion auf verschiedene Arten implementiert werden. Zum Beispiel zwei offensichtliche:

load r1 = *b
copy r2 = r1
increment r1
store *b = r1
store *a = r2

oder

load r1 = *b
store *a = r1
increment r1
store *b = r1

Beachten Sie, dass die erste oben aufgeführte Anweisung, die mehr Anweisungen und mehr Register verwendet, in allen Fällen verwendet werden muss, in denen sich a und b nicht nachweisen lassen.

Random832
quelle
Sie zeigen in der Tat einen Fall, in dem mein Vorschlag zu mehr Maschinenoperationen führt, der für mich jedoch unbedeutend ist. Und der Compiler hat immer noch einige Freiheiten - die einzige wirkliche Anforderung, die ich hinzufüge, ist, bvorher zu speichern a.
Ugoren
3

Erbe

Die Annahme, dass C heute neu erfunden werden könnte, kann nicht gelten. Es gibt so viele Zeilen mit C-Codes, die produziert und täglich verwendet werden, dass es einfach falsch ist, die Spielregeln mitten im Spiel zu ändern.

Natürlich können Sie mit Ihren Regeln eine neue Sprache erfinden, beispielsweise C + = . Aber das wird nicht C sein.

mouviciel
quelle
2
Ich glaube nicht, dass wir C heute neu erfinden können. Dies bedeutet nicht, dass wir diese Themen nicht diskutieren können. Was ich jedoch vorschlage, ist nicht wirklich neu zu erfinden. Das Konvertieren von undefiniertem Verhalten in definiertes oder nicht angegebenes Verhalten kann beim Aktualisieren eines Standards durchgeführt werden. Die Sprache wäre weiterhin C.
ugoren
2

Wenn Sie festlegen, dass etwas definiert ist, werden die vorhandenen Compiler nicht dahingehend geändert, dass Ihre Definition eingehalten wird. Dies gilt insbesondere für eine Annahme, auf die an vielen Stellen explizit oder implizit zurückgegriffen werden kann.

Das Hauptproblem bei der Annahme liegt nicht bei x = x++;(Compiler können es leicht überprüfen und sollten warnen), sondern bei *p1 = (*p2)++und gleichwertig (p1[i] = p2[j]++; wenn p1 und p2 Parameter für eine Funktion sind), wenn der Compiler nicht leicht weiß, ob p1 == p2(in C99) restrictwurde hinzugefügt, um die Möglichkeit der Annahme von p1! = p2 zwischen Sequenzpunkten zu verteilen, weshalb die Optimierungsmöglichkeiten für wichtig erachtet wurden.

Ein Programmierer
quelle
Ich verstehe nicht, inwiefern sich durch meinen Vorschlag etwas ändert p1[i]=p2[j]++. Wenn der Compiler kein Aliasing annehmen kann, gibt es kein Problem. Wenn dies nicht möglich ist, muss es nach dem Buch sortiert werden - p2[j]zuerst inkrementieren , p1[i]später speichern . Abgesehen von den Optimierungsmöglichkeiten, die nicht signifikant erscheinen, sehe ich kein Problem.
Ugoren
Der zweite Absatz war nicht unabhängig vom ersten, sondern ein Beispiel für die Art von Orten, an denen sich die Annahme einschleichen kann und schwer zu verfolgen sein wird.
Programmierer
Der erste Absatz sagt etwas ganz Offensichtliches aus - Compiler müssen geändert werden, um einem neuen Standard zu entsprechen. Ich glaube nicht wirklich, dass ich eine Chance habe, dies zu standardisieren und die Compiler-Autoren dazu zu bringen, dem zu folgen. Ich denke, es lohnt sich zu diskutieren.
Ugoren
Das Problem ist nicht, dass man die Compiler ändern muss, wenn sich die Sprache ändert, sondern dass die Änderungen allgegenwärtig und schwer zu finden sind. Der praktischste Ansatz wäre wahrscheinlich, das Zwischenformat zu ändern, auf dem der Optimierer arbeitet, dh so zu tun, als wäre x = x++;es nicht geschrieben worden, t = x; x++; x = t;oder x=x; x++;oder was auch immer Sie als semantisch wollen (aber was ist mit der Diagnose?). Für eine neue Sprache lassen Sie einfach die Nebenwirkungen fallen.
Programmierer
Ich weiß nicht zu viel über die Compiler-Struktur. Wenn ich wirklich alle Compiler ändern wollte, würde es mich mehr interessieren. Aber vielleicht x++als Sequenzpunkt behandeln , als ob es ein Funktionsaufruf inc_and_return_old(&x)wäre , würde den Trick tun.
Ugoren
-1

In manchen Fällen ist diese Art von Code wurde in der neuen C ++ definiert 11 Standard.

DeadMG
quelle
5
Möchten Sie näher darauf eingehen?
Ugoren
Ich denke, x = ++xist jetzt gut definiert (aber nicht x = x++)
MM