Bevor wir auf die Frage eingehen, was los ist, ist es wichtig darauf hinzuweisen, dass das Programm gemäß Fehlerbericht 1886: Sprachverknüpfung für main () schlecht geformt ist :
[...] Ein Programm, das eine Variable main im globalen Bereich deklariert oder den Namen main mit C-Sprachverknüpfung (in einem beliebigen Namespace) deklariert, ist fehlerhaft. [...]
Die neuesten Versionen von clang und gcc machen dies zu einem Fehler und das Programm wird nicht kompiliert ( siehe gcc live-Beispiel ):
error: cannot declare '::main' to be a global variable
int main = ( std::cout << "C++ is excellent!\n", 195 );
^
Warum gab es in älteren Versionen von gcc und clang keine Diagnose? Dieser Fehlerbericht hatte erst Ende 2014 einen Lösungsvorschlag, so dass dieser Fall erst vor kurzem explizit falsch formuliert wurde, was eine Diagnose erfordert.
Vor dieser Zeit scheint es so nicht definiertes Verhalten wäre , da wir ein verstoßen wird Anforderung des Entwurfs C ++ Standard aus dem Abschnitt 3.6.1
[basic.start.main] :
Ein Programm muss eine globale Funktion namens main enthalten, die den festgelegten Start des Programms darstellt. [...]
Undefiniertes Verhalten ist unvorhersehbar und erfordert keine Diagnose. Die Inkonsistenz, die wir bei der Reproduktion des Verhaltens sehen, ist ein typisches undefiniertes Verhalten.
Was macht der Code eigentlich und warum führt er in einigen Fällen zu Ergebnissen? Mal sehen, was wir haben:
declarator
| initializer----------------------------------
| | |
v v v
int main = ( std::cout << "C++ is excellent!\n", 195 );
^ ^ ^
| | |
| | comma operator
| primary expression
global variable of type int
Wir haben main
ein int , das im globalen Namespace deklariert ist und initialisiert wird. Die Variable hat eine statische Speicherdauer. Es ist die Implementierung definiert, ob die Initialisierung stattfinden soll, bevor ein Aufruf main
versucht wird, aber es scheint, dass gcc dies vor dem Aufruf tut main
.
Der Code verwendet den Komma-Operator , der linke Operand ist ein Ausdruck für verworfene Werte und wird hier ausschließlich für den Nebeneffekt des Aufrufs verwendet std::cout
. Das Ergebnis des Kommaoperators ist der richtige Operand, in diesem Fall der Wert, 195
der der Variablen zugewiesen ist main
.
Wir können sehen, dass sergej auf die generierten Assemblyshows hinweist, die cout
während der statischen Initialisierung aufgerufen werden. Obwohl der interessantere Diskussionspunkt in der Live-Godbolt-Sitzung folgender wäre:
main:
.zero 4
und die folgenden:
movl $195, main(%rip)
Das wahrscheinliche Szenario ist, dass das Programm zu dem Symbol springt und main
erwartet, dass gültiger Code vorhanden ist, und in einigen Fällen einen Seg-Fehler aufweist . Wenn dies der Fall ist, würden wir erwarten, dass das Speichern von gültigem Maschinencode in der Variablen main
zu einem funktionsfähigen Programm führen könnte , vorausgesetzt, wir befinden uns in einem Segment, das die Codeausführung ermöglicht. Wir können sehen, dass dieser IOCCC-Eintrag von 1984 genau das tut .
Es scheint, dass wir gcc dazu bringen können, dies in C zu tun, indem wir ( live sehen ):
const int main = 195 ;
Es gibt Fehler, wenn die Variable main
vermutlich nicht const ist, weil sie sich nicht an einem ausführbaren Speicherort befindet. Hat Tipp zu diesem Kommentar hier, der mir diese Idee gegeben hat.
Siehe auch FUZxxl Antwort hier auf eine C-spezifische Version dieser Frage.