Warum führt const int main = 195 zu einem funktionierenden Programm, aber ohne const endet es in einem Segmentierungsfehler?

Lesezeit: 5 Minuten

Benutzer-Avatar
Destruktor

Ziehen Sie das folgende C-Programm in Betracht (siehe Live-Demo hier).

const int main = 195;

Ich weiß, dass in der realen Welt kein Programmierer solchen Code schreibt, weil er keinen nützlichen Zweck erfüllt und keinen Sinn ergibt. Aber wenn ich die entferne const Schlüsselwort von oben das Programm führt es sofort zu a Segmentierungsfehler. Wieso den? Ich bin gespannt, den Grund dafür zu erfahren.

GCC 4.8.2 gibt beim Kompilieren folgende Warnung aus.

Warnung: ‘main’ ist normalerweise eine Funktion [-Wmain]

const int main = 195;
          ^

Warum das Vorhandensein und Fehlen von const Stichwort: Unterschied hier im Verhalten des Programms?

  • Laut Standard ist dies einfach ein undefiniertes Verhalten.

    – Melpomen

    23. Oktober 2015 um 15:04 Uhr

  • @machine_1 195 ist die Kodierung für den Opcode ret (Rückkehr von der Funktion) auf 8086 und seinen Nachfolgern. Sie können sich vorstellen, was passiert, wenn Sie das in eine Variable einfügen und diese Variable als Funktion aufrufen.

    – fuz

    23. Oktober 2015 um 15:29 Uhr

  • Es ist wahrscheinlich relevant, auf Wie kann ein Programm mit einer globalen Variable namens main anstelle einer main-Funktion zu verlinken?

    – Shafik Yaghmour

    23. Oktober 2015 um 15:30 Uhr

  • Haben Sie den Wert bewusst so gewählt, dass er übereinstimmt? ret Anweisung?

    – Ruslan

    24. Oktober 2015 um 15:37 Uhr

  • @Ruslan Wenn Sie etwas suchen, finden Sie an mehreren Stellen verschiedene Versionen davon. Auf dem Stack-Exchange-Netzwerk Dies war eine der älteren Referenzen. In meiner Antwort auf den obigen Link finden wir einen IOCCC-Eintrag von 1984, der etwas Ähnliches tut, aber viel ausgefeilter ist.

    – Shafik Yaghmour

    24. Oktober 2015 um 23:22 Uhr


Benutzer-Avatar
fuz

Beobachten Sie, wie der Wert 195 dem entspricht ret (Rückkehr von der Funktion) Anweisung auf 8086-kompatiblen Geräten. Diese Definition von main verhält sich also so, als ob Sie es so definiert hätten int main() {} wenn hingerichtet.

Auf manchen Plattformen const Daten werden in einen ausführbaren, aber nicht beschreibbaren Speicherbereich geladen, während veränderliche Daten (dh Daten nicht qualifiziert const) wird in einen beschreibbaren, aber nicht ausführbaren Speicherbereich geladen. Aus diesem Grund „funktioniert“ das Programm, wenn Sie es deklarieren main wie const aber nicht wenn du das weglässt const Qualifikation.

Traditionell enthielten Binärdateien drei Segmente:

  • Das text Segment ist (falls von der Architektur unterstützt) schreibgeschützt und ausführbar und enthält ausführbaren Code, Variablen von statisch Speicherdauer qualifiziert constund Zeichenfolgenliterale
  • Das data Segment ist beschreibbar und kann nicht ausgeführt werden. Es enthält nicht qualifizierte Variablen const mit statisch Speicherdauer und (zur Laufzeit) Objekte mit zugeteilt Speicherdauer
  • Das bss Segment ist ähnlich wie die data Segment, wird aber mit lauter Nullen initialisiert. Es enthält Variablen von statisch Speicherdauer nicht qualifiziert const die ohne Initialisierer deklariert wurden
  • Das stack Segment ist in der Binärdatei nicht vorhanden und enthält Variablen mit automatisch Speicherdauer

Entferne den const Qualifizierer aus der Variablen main bewirkt, dass es von verschoben wird text zum data segment, das nicht ausführbar ist und die von Ihnen beobachtete Segmentierungsverletzung verursacht.

Moderne Plattformen haben oft weitere Segmente (zB a rodata Segment für Daten, die weder beschreibbar noch ausführbar sind), also verstehen Sie dies bitte nicht als genaue Beschreibung Ihrer Plattform, ohne die plattformspezifische Dokumentation zu konsultieren.

Bitte haben Sie Verständnis dafür, dass dies nicht der Fall ist main eine Funktion ist in der Regel fehlerhaft, obwohl eine Plattform dies technisch zulassen könnte main als Variable zu deklarieren, vgl. ISO 9899:2011 §5.1.2.2.1 ¶1, Hervorhebung von mir:

1 Die beim Programmstart aufgerufene Funktion wird benannt main. Die Implementierung deklariert keinen Prototyp für diese Funktion. Es muss mit einem Rückgabetyp von definiert werden int und ohne Parameter (…) oder mit zwei Parametern (…) oder gleichwertig; oder auf eine andere implementierungsdefinierte Weise.

  • Einige gute Punkte, zwol hat einige davon hier in den Kommentaren zu meiner Antwort auf eine ähnliche C++-Version dieser Frage angesprochen

    – Shafik Yaghmour

    23. Oktober 2015 um 15:32 Uhr


  • Bitte geben Sie in Ihrer Antwort Ihren Kommentar zur Codierung für den Opcode ret an. Dies ist der Schlüssel zum Verständnis des beschriebenen Verhaltens.

    – Dekay

    23. Oktober 2015 um 19:40 Uhr

  • @ user19474 Besser so?

    – fuz

    23. Oktober 2015 um 19:45 Uhr

  • @FUZxxl: nette Antwort. Aber Ihre Antwort erklärt nicht, warum das Programm mit einem Garbage-Wert als Rückgabestatus anstelle von 0 beendet wird? Es wäre besser, wenn Sie den Grund dafür nennen könnten.

    – Destruktor

    15. Dezember 2015 um 16:36 Uhr

  • @PravasiMeet The ret Anweisung beendet die aktuelle Funktion. Es setzt keinen Rückgabewert. Somit beendet sich das Programm mit dem, was in war eax damals anmelden main zurückgegeben, dh ein zufälliger Garbage-Wert.

    – fuz

    15. Dezember 2015 um 16:49 Uhr


Benutzer-Avatar
Bathseba

In C, main auf globaler Ebene ist fast immer eine Funktion.

Benutzen main als Variable im globalen Geltungsbereich macht das Verhalten des Programms undefiniert.

(Es gerade könnte der Fall sein, dass, wenn Sie schreiben const Der Compiler optimiert die Variable zu einer Konstanten und Ihr Programmverhalten ist daher anders. Aber das Programmverhalten ist still nicht definiert).

  • Nö. Eine Plattform kann dies zulassen main „in einer durchsetzungsdefinierten Weise“ zu deklarieren.

    – fuz

    23. Oktober 2015 um 15:10 Uhr

  • Ich bin zu alt, um den Standard zu durchforsten, aber ich denke, es muss sein stets eine Funktion sein!

    – Bathseba

    23. Oktober 2015 um 15:10 Uhr

  • Vgl. ISO 9899:2011 §5.1.2.2.1 „Die beim Programmstart aufgerufene Funktion heißt main. Die Implementierung deklariert keinen Prototyp für diese Funktion. Es muss mit einem Rückgabetyp von int und ohne Parameter definiert werden: (…) oder mit zwei Parametern (hier als argc und argv bezeichnet, obwohl beliebige Namen verwendet werden können, da sie lokal für die Funktion sind, in der sie enthalten sind deklariert werden): (…) oder gleichwertig; 10) oder auf eine andere implementierungsdefinierte Weise.“

    – fuz

    23. Oktober 2015 um 15:11 Uhr

  • OK. Gute Standardreferenz! Ich habe geändert.

    – Bathseba

    23. Oktober 2015 um 15:15 Uhr

1383570cookie-checkWarum führt const int main = 195 zu einem funktionierenden Programm, aber ohne const endet es in einem Segmentierungsfehler?

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

Privacy policy