Betrachten Sie folgendes Programm:
#include <iostream>
int main = ( std::cout << "C++ is excellent!\n", 195 );
Unter Verwendung von g ++ 4.8.1 (mingw64) unter Windows 7 kompiliert das Programm und läuft einwandfrei. Es druckt:
C ++ ist ausgezeichnet!
zur Konsole. main
scheint eher eine globale Variable als eine Funktion zu sein; Wie kann dieses Programm ohne die Funktion ausgeführt werden main()
? Entspricht dieser Code dem C ++ - Standard? Ist das Verhalten des Programms gut definiert? Ich habe die -pedantic-errors
Option auch verwendet, aber das Programm wird immer noch kompiliert und ausgeführt.
c++
main
language-lawyer
Zerstörer
quelle
quelle
195
der Opcode für dieRET
Anweisung ist und dass der Aufrufer in der C-Aufrufkonvention den Stapel löscht.main()
Funktion? Tatsächlich sind sie völlig unabhängig.)int main = ( std::cout << "C++ is excellent!\n", exit(0),1 );
(und einbeziehe<cstdlib>
), obwohl das Programm rechtlich schlecht gestaltet ist.Antworten:
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 :
Die neuesten Versionen von clang und gcc machen dies zu einem Fehler und das Programm wird nicht kompiliert ( siehe gcc live-Beispiel ):
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] :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:
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 Aufrufmain
versucht wird, aber es scheint, dass gcc dies vor dem Aufruf tutmain
.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 istmain
.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:und die folgenden:
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 Variablenmain
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 ):
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.
quelle
main
es sich nicht um eine reservierte Kennung handelt (3.6.1 / 3). In diesem Fall denke ich, dass die Behandlung dieses Falls durch VS2013 (siehe Francis Cuglers Antwort) in der Behandlung korrekter ist als bei gcc & clang.Ab 3.6.1 / 1:
Aus diesem Grund sieht es so aus, als würde g ++ ein Programm (vermutlich als "freistehende" Klausel) ohne Hauptfunktion zulassen.
Dann ab 3.6.1 / 3:
Hier erfahren wir also, dass es vollkommen in Ordnung ist, eine ganzzahlige Variable mit dem Namen zu haben
main
.Wenn Sie sich schließlich fragen, warum die Ausgabe gedruckt wird, verwendet die Initialisierung von den
int main
Kommaoperator, umcout
bei statischer Init auszuführen, und gibt dann einen tatsächlichen Integralwert für die Initialisierung an.quelle
main
in etwas anderes umbenennen :(.text+0x20): undefined reference to
main '`gcc 4.8.1 generiert die folgende x86-Assembly:
Beachten Sie, dass dies
cout
während der Initialisierung aufgerufen wird, nicht in dermain
Funktion!.zero 4
deklariert 4 (0-initialisierte) Bytes ab Positionmain
, wobeimain
der Name der Variablen [!] ist .Das
main
Symbol wird als Programmstart interpretiert. Das Verhalten hängt von der Plattform ab.quelle
195
der Opcode fürret
einige Architekturen ist. Das Sagen von Null-Anweisungen ist daher möglicherweise nicht korrekt.Das ist ein schlecht geformtes Programm. Es stürzt in meiner Testumgebung cygwin64 / g ++ 4.9.3 ab.
Aus dem Standard:
quelle
Der Grund, warum ich glaube, dass dies funktioniert, ist, dass der Compiler nicht weiß, dass er die
main()
Funktion kompiliert, sodass er eine globale Ganzzahl mit Zuweisungsnebeneffekten kompiliert.Das Objektformat, in das diese Übersetzungseinheit kompiliert wird, kann nicht zwischen einem Funktionssymbol und einem Variablensymbol unterscheiden .
So ist der Linker verbindet glücklich mit dem (variabel) Hauptsymbol und behandelt es wie ein Funktionsaufruf. Aber erst, wenn das Laufzeitsystem den globalen Variableninitialisierungscode ausgeführt hat.
Als ich das Beispiel laufen ließ, wurde es ausgedruckt, aber dann verursachte es einen Seg-Fehler . Ich gehe davon aus, dass das Laufzeitsystem versucht hat, eine int-Variable so auszuführen , als wäre es eine Funktion .
quelle
Ich habe dies auf einem Win7 64-Bit-Betriebssystem mit VS2013 versucht und es wird korrekt kompiliert, aber wenn ich versuche, die Anwendung zu erstellen, erhalte ich diese Meldung aus dem Ausgabefenster.
quelle
main()
da es sich um eine Variable vom Typ istint
Sie machen hier knifflige Arbeit. Als Haupt (irgendwie) könnte als Ganzzahl deklariert werden. Sie haben den Listenoperator verwendet, um die Nachricht zu drucken und ihr dann 195 zuzuweisen. Wie von jemandem unten gesagt, ist es wahr, dass es mit C ++ nicht tröstet. Da der Compiler jedoch keinen benutzerdefinierten Namen gefunden hat, hat er sich nicht beschwert. Denken Sie daran, dass main keine systemdefinierte Funktion ist. Die benutzerdefinierte Funktion und das Element, von dem aus das Programm gestartet wird, ist das Hauptmodul, nicht main (). Wieder wird main () von der Startfunktion aufgerufen, die vom Loader absichtlich ausgeführt wird. Dann werden alle Ihre Variablen initialisiert und während der Initialisierung wird die Ausgabe so ausgeführt. Das ist es. Programm ohne main () ist in Ordnung, aber nicht Standard.
quelle