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?
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 const
und 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.
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).
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