C++ Kompilierungsfehler?

Lesezeit: 8 Minuten

C Kompilierungsfehler
eivour

Ich habe folgenden Code:

#include <iostream>
#include <complex>
using namespace std;

int main() {
    complex<int> delta;
    complex<int> mc[4] = {0};

    for(int di = 0; di < 4; di++, delta = mc[di]) {
        cout << di << endl;
    }

    return 0;
}

Ich erwarte, dass es “0, 1, 2, 3” ausgibt und stoppt, aber es gibt eine endlose Reihe von “0, 1, 2, 3, 4, 5, …..” aus.

So sieht der Vergleich aus di<4 funktioniert nicht gut und gibt immer true zurück.

Wenn ich nur auskommentiere ,delta=mc[di], bekomme ich ganz normal “0, 1, 2, 3”. Was ist das Problem mit dem unschuldigen Auftrag?

ich benutze Ideone.com g++ C++14 mit Option -O2.

  • Ich habe es mit g++ versucht, und ohne Optimierungen funktioniert es gut. Mit -O3 gibt es das vom OP erwähnte Verhalten. Mit -O1 ist es ok.

    – Chris-Karte

    10. September 2015 um 16:05 Uhr


  • Klingt nach aggressiver Loop-Optimierung aufgrund undefinierten Verhaltens wie in diesem Fall

    – Shafik Yaghmour

    10. September 2015 um 16:07 Uhr

  • Obwohl der Code undefiniertes Verhalten aufruft, ist die Optimierung ziemlich aggressiv, und wie ich beobachte, hilft es nicht, dass gcc nicht konsequent eine Warnung für diese Optimierung ausgeben kann.

    – Shafik Yaghmour

    10. September 2015 um 20:11 Uhr

  • @Shafik Yaghmour Der Grund, warum GCC keine Warnung mit ausgibt cout << di liegt wohl daran, dass der stream-insertion-operator für komplexe die adresse übergibt di zu einem “undurchsichtigen” Code (oder dass der Stream-Einfügungsoperator für Komplexe selbst undurchsichtig ist – was mich jedoch überraschen würde). Und abhängig davon, was dieser “undurchsichtige” Code tut, könnte das Verhalten des Programms immer noch gut definiert werden. Ich sage nicht, dass es unmöglich wäre, in diesem Fall ohne zu viele Fehlalarme (oder sogar Fehlalarme) zu warnen. Nur, dass es ziemlich schwer werden würde.

    – Paul Groke

    11. September 2015 um 1:05 Uhr


  • @ShafikYaghmour Ich verwende Ideone.com g++ C++14 mit der Option -O2. (Danke, ich habe es in meiner Frage hinzugefügt.)

    – evour

    11. September 2015 um 11:42 Uhr


C Kompilierungsfehler
Shafik Yaghmur

Dies liegt an undefiniertem Verhalten, Sie greifen auf das Array zu mc außerhalb der Grenzen bei der letzten Iteration Ihrer Schleife. Einige Compiler können eine aggressive Schleifenoptimierung unter der Annahme durchführen, dass kein undefiniertes Verhalten vorliegt. Die Logik wäre ähnlich wie die folgende:

  • Zugriff mc Out of Bounds ist undefiniertes Verhalten
  • Nehmen Sie kein undefiniertes Verhalten an
  • Deswegen di < 4 ist immer wahr, da sonst mc[di] würde undefiniertes Verhalten hervorrufen

gcc mit aktivierter Optimierung und Verwendung der -fno-aggressive-loop-optimizations Flag bewirkt, dass das Verhalten der Endlosschleife verschwindet (live sehen). Während ein Live-Beispiel mit Optimierung, aber ohne -fno-aggressive-loop-optimizations zeigt das von Ihnen beobachtete Endlosschleifenverhalten.

EIN Godbolt Live-Beispiel des Codes zeigt die di < 4 check wird entfernt und durch ein bedingungsloses jmp ersetzt:

jmp .L6

Dies ist fast identisch mit dem in skizzierten Fall GCC vor 4.8 bricht defekte SPEC 2006-Benchmarks. Die Kommentare zu diesem Artikel sind ausgezeichnet und es lohnt sich, sie zu lesen. Es stellt fest, dass Clang den Fall in dem Artikel mit gefangen hat -fsanitize=undefined was ich für diesen Fall nicht reproduzieren kann, aber gcc verwende -fsanitize=undefined tut (live sehen). Der wahrscheinlich berüchtigtste Fehler bei einem Optimierer, der Rückschlüsse auf undefiniertes Verhalten zieht, ist der Entfernen der Nullzeigerprüfung des Linux-Kernels.

Obwohl dies eine aggressive Optimierung ist, ist es wichtig zu beachten, dass, wie der C++-Standard sagt, undefiniertes Verhalten ist:

Verhalten, für das diese Internationale Norm keine Anforderungen stellt

Was im Wesentlichen bedeutet, dass alles möglich ist, und es stellt fest (Betonung von mir):

[…]Zulässiges undefiniertes Verhalten reicht vom völligen Ignorieren der Situation mit unvorhersehbaren Ergebnissensich während der Übersetzung oder Programmausführung in einer für die Umgebung charakteristischen dokumentierten Weise zu verhalten (mit oder ohne Ausgabe einer Diagnosemeldung), zum Abbruch einer Übersetzung oder Ausführung (mit Ausgabe einer Diagnosemeldung).[…]

Um eine Warnung von gcc zu erhalten, müssen wir die verschieben cout außerhalb der Schleife und dann sehen wir die folgende Warnung (live sehen):

warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
     for(di=0; di<4;di++,delta=mc[di]){ }
     ^

was wahrscheinlich ausgereicht hätte, um dem OP genügend Informationen zu liefern, um herauszufinden, was los war. Inkonsistenzen wie diese sind typisch für die Verhaltenstypen, die wir bei undefiniertem Verhalten sehen können. Um ein besseres Verständnis dafür zu bekommen, warum eine solche Warnung angesichts eines undefinierten Verhaltens widersprüchlich sein kann Warum können Sie nicht warnen, wenn Sie auf der Grundlage von undefiniertem Verhalten optimieren? ist gut zu lesen.

Notiz, -fno-aggressive-loop-optimizations ist dokumentiert in der Versionshinweise zu gcc 4.8.

  • Dies. Bei undefiniertem Verhalten geht es nicht (nur) um Abstürze. Es geht um Annahmen, und wenn Sie gegen die Annahmen des Compilers (wie in der Sprachspezifikation definiert) verstoßen, sind alle Wetten ungültig …

    – Matthias M.

    10. September 2015 um 19:15 Uhr

  • @MatthieuM. Tatsächlich kommen wir immer wieder auf dieses Thema zurück, einige Zitate in meiner Antwort hier sind ebenfalls relevant.

    – Shafik Yaghmour

    10. September 2015 um 20:45 Uhr

  • Was für ein Idiot von mir, ich habe nicht bemerkt, dass ich auf mc zugreife[4] 😉 Ich habe Ideone.com verwendet, also würde ich sowieso keine Warnung bekommen. Das nächste Mal werde ich einen Editor verwenden, der mich warnt, selbst wenn die Kompilierung erfolgreich ist 🙂

    – evour

    11. September 2015 um 11:32 Uhr


  • @eivour Es kann hilfreich sein, einen Link zu Ihrem Online-Beispiel bereitzustellen. In diesem Fall denke ich, dass jedes Mal, wenn Sie ein Beispiel in ideone ausführen, eine URL zu diesem Beispiel bereitgestellt wird. Ich persönlich nutze lieber Coliru oder Zauberstabbox die beide einen Share-Button bieten.

    – Shafik Yaghmour

    11. September 2015 um 11:48 Uhr

  • gcc 4.9.3 erzeugt immer noch keine Warnung: g++ -Waggressive-loop-optimizations -Wall -Wextra -O3 test.cpp – Laut dieser Seite sollte der Compiler eine Warnung anzeigen: gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

    – Peter VARGA

    16. September 2015 um 7:42 Uhr


Da Sie inkrementieren di bevor Sie es zum Indexieren verwenden mcbeim vierten Mal durch die Schleife verweisen Sie auf mc[4]die hinter dem Ende Ihres Arrays liegt, was wiederum zu problematischem Verhalten führen kann.

  • Ignorieren Sie meinen letzten Kommentar, das könnte ein Problem mit ideone.com gewesen sein, das meinen Code nach der Bearbeitung nicht ordnungsgemäß erneut ausgeführt hat. Ein weiterer Test hat funktioniert: ideone.com/vLtvcy

    – zwischenjay

    10. September 2015 um 16:11 Uhr


  • Entweder verwenden di++,delta=mc[di-1] oder delta=mc[di],di++ behebt das Problem auch. Sieht so aus, als hätte Logicrat recht.

    – KompjoeFriek

    10. September 2015 um 16:15 Uhr

  • Vielleicht gibt es einen Off-by-One-Fehler, und eivour meinte delta=mc[di++] eher, als delta=mc[++di]um alle zu verwenden mc Werte?

    – Toby Speight

    10. September 2015 um 16:21 Uhr


C Kompilierungsfehler
Franz Cugler

Du hast das:

for(int di=0; di<4; di++, delta=mc[di]) {
  cout<<di<<endl;
}

Versuchen Sie stattdessen Folgendes:

for(int di=0; di<4; delta=mc[di++]) {
   cout<<di<<endl;
}

BEARBEITEN:

Um zu verdeutlichen, was vor sich geht, lassen Sie uns die Iteration Ihrer For-Schleife aufschlüsseln:

1. Iteration: Zunächst wird di auf 0 gesetzt. Vergleichsprüfung: Ist di kleiner als 4? Ja okay weiter. Erhöhen Sie di um 1. Jetzt ist di = 1. Greifen Sie das “n-te” Element von mc[] und setze es auf Delta. Dieses Mal greifen wir das 2. Element, da dieser indizierte Wert 1 und nicht 0 ist. Führen Sie schließlich den/die Codeblock/s innerhalb der for-Schleife aus.

2. Iteration: Jetzt wird di auf 1 gesetzt. Vergleichsprüfung: Ist di kleiner als 4? Ja und weiter. Inkrementiere di um 1. Jetzt ist di = 2. Nimm das “n-te” Element von mc[] und setze es auf Delta. Dieses Mal greifen wir das 3. Element, da dieser indizierte Wert 2 ist. Führen Sie schließlich den/die Codeblock/s innerhalb der for-Schleife aus.

3. Iteration: Jetzt wird di auf 2 gesetzt. Vergleichsprüfung: Ist di kleiner als 4? Ja und weiter. Erhöhen Sie di um 1. Jetzt ist di = 3. Greifen Sie das “n-te” Element von mc[] und setze es auf Delta. Dieses Mal greifen wir das 4. Element, da dieser indizierte Wert 3 ist. Führen Sie schließlich den/die Codeblock/s innerhalb der for-Schleife aus.

4. Iteration: Jetzt wird di auf 3 gesetzt. Vergleichsprüfung: Ist di kleiner als 4? Ja und weiter. Inkrementiere di um 1. Jetzt ist di = 4. (Kannst du sehen, wohin das führt?) Nimm das “n-te” Element von mc[] und setze es auf Delta. Dieses Mal greifen wir das 5. Element, da dieser indizierte Wert 4 ist. Uh Oh, wir haben ein Problem; unsere Array-Größe beträgt nur 4. Delta hat jetzt Müll und dies ist ein undefiniertes Verhalten oder eine Beschädigung. Führen Sie schließlich den/die Codeblock/s innerhalb der for-Schleife mit “Garbage Delta” aus.

5. Iteration. Jetzt wird di auf 4 gesetzt. Vergleichsprüfung: Ist di kleiner als 4? Nein, aus der Schleife ausbrechen.

Beschädigung durch Überschreiten der Grenzen des zusammenhängenden Speichers (Array).

1646256616 347 C Kompilierungsfehler
PaulHK

Das liegt daran, dass di++ beim letzten Durchlauf der Schleife ausgeführt wird.

Zum Beispiel;

int di = 0;
for(; di < 4; di++);
// after the loop di == 4
// (inside the loop we see 0,1,2,3)
// (inside the for statement, after di++, we see 1,2,3,4)

Sie greifen auf mc zu[] Wenn di == 4 ist, ist es ein Out-of-Bounds-Problem, das möglicherweise einen Teil des Stacks zerstört und die Variable di beschädigt.

eine lösung wäre:

for(int di = 0; di < 4; di++) {
    cout << di << endl;
    delta = mc[di];
}

916320cookie-checkC++ Kompilierungsfehler?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy