Ich weiß, dass es zwei verschiedene Signaturen gibt, um die Hauptmethode zu schreiben -
int main()
{
//Code
}
oder für die Behandlung von Befehlszeilenargumenten schreiben wir es als
int main(int argc, char * argv[])
{
//code
}
In C++
Ich weiß , wir eine Methode überlasten können, aber in C
wie funktioniert der Compiler diese zwei verschiedene Signaturen von handhaben main
Funktion?
main
Methode in einem einzelnen Programm inC
(oder wirklich in so ziemlich jeder Sprache mit einem solchen Konstrukt) haben.main
- empfehle ich John R. Levines klassisches Buch "Linkers & Loaders".int main(void)
nichtint main()
(obwohl ich noch nie einen Compiler gesehen habe, der dasint main()
Formular ablehnt ).()
Formular ist veraltet und es ist nicht klar, ob es überhaupt zulässig istmain
(es sei denn, die Implementierung dokumentiert es ausdrücklich als zulässiges Formular). Der C-Standard (siehe 5.1.2.2.1 Programmstart) erwähnt das()
Formular nicht, was dem Formular nicht ganz entspricht()
. Die Details sind zu lang für diesen Kommentar.Antworten:
Einige der Funktionen der C-Sprache begannen als Hacks, die gerade funktionierten.
Eine dieser Funktionen sind mehrere Signaturen für Hauptargumentlisten sowie Argumentlisten mit variabler Länge.
Programmierer haben festgestellt, dass sie zusätzliche Argumente an eine Funktion übergeben können und mit ihrem angegebenen Compiler nichts Schlimmes passiert.
Dies ist der Fall, wenn die Aufrufkonventionen so sind, dass:
Eine Reihe von Aufrufkonventionen, die diesen Regeln entsprechen, ist die stapelbasierte Parameterübergabe, bei der der Aufrufer die Argumente einfügt und sie von rechts nach links verschoben werden:
In Compilern, in denen diese Art von Aufrufkonvention der Fall ist, muss nichts Besonderes getan werden, um die beiden Arten
main
oder sogar zusätzliche Arten zu unterstützen.main
kann eine Funktion ohne Argumente sein. In diesem Fall werden die Elemente, die auf den Stapel geschoben wurden, nicht berücksichtigt. Wenn es eine Funktion von zwei Argumenten ist, findet esargc
undargv
als die beiden obersten Stapelelemente. Wenn es sich um eine plattformspezifische Variante mit drei Argumenten und einem Umgebungszeiger (eine allgemeine Erweiterung) handelt, funktioniert dies ebenfalls: Das dritte Argument wird als drittes Element oben im Stapel gefunden.Ein fester Aufruf funktioniert also in allen Fällen, sodass ein einzelnes festes Startmodul mit dem Programm verknüpft werden kann. Dieses Modul könnte in C geschrieben werden, als eine Funktion, die dieser ähnelt:
Mit anderen Worten, dieses Startmodul ruft immer nur ein Haupt mit drei Argumenten auf. Wenn main keine oder nur keine Argumente
int, char **
akzeptiert, funktioniert es aufgrund der aufrufenden Konventionen einwandfrei und wenn keine Argumente akzeptiert werden.Wenn Sie so etwas in Ihrem Programm tun würden, wäre es nicht portierbar und würde von ISO C als undefiniertes Verhalten angesehen: Deklarieren und Aufrufen einer Funktion auf eine Weise und Definieren in einer anderen. Der Starttrick eines Compilers muss jedoch nicht portabel sein. Es richtet sich nicht nach den Regeln für tragbare Programme.
Angenommen, die Aufrufkonventionen sind so, dass sie nicht so funktionieren können. In diesem Fall muss der Compiler
main
speziell behandeln . Wenn es bemerkt, dass es diemain
Funktion kompiliert , kann es Code generieren, der beispielsweise mit einem Aufruf mit drei Argumenten kompatibel ist.Das heißt, Sie schreiben Folgendes:
Wenn der Compiler es sieht, führt er im Wesentlichen eine Codetransformation durch, sodass die von ihm kompilierte Funktion folgendermaßen aussieht:
außer dass die Namen
__argc_ignore
nicht buchstäblich existieren. In Ihren Bereich werden keine solchen Namen aufgenommen, und es wird keine Warnung vor nicht verwendeten Argumenten angezeigt. Die Codetransformation bewirkt, dass der Compiler Code mit der richtigen Verknüpfung ausgibt, die weiß, dass drei Argumente bereinigt werden müssen.Eine andere Implementierungsstrategie besteht darin, dass der Compiler oder Linker die
__start
Funktion (oder wie auch immer sie genannt wird) benutzerdefiniert generiert oder zumindest eine aus mehreren vorkompilierten Alternativen auswählt. In der Objektdatei können Informationen darüber gespeichert werden, welche der unterstützten Formenmain
verwendet werden. Der Linker kann sich diese Informationen ansehen und die richtige Version des Startmoduls auswählen, die einen Aufruf enthält,main
der mit der Programmdefinition kompatibel ist. C-Implementierungen haben normalerweise nur eine geringe Anzahl unterstützter Formen,main
sodass dieser Ansatz möglich ist.Compiler für die C99-Sprache müssen immer bis
main
zu einem gewissen Grad speziell behandeln , um den Hack zu unterstützen, dassreturn
das Verhalten soreturn 0
ausgeführt wird, als ob sie ausgeführt würden , wenn die Funktion ohne Anweisung beendet wird . Dies kann wiederum durch eine Codetransformation behandelt werden. Der Compiler bemerkt, dass eine aufgerufene Funktionmain
kompiliert wird. Dann wird geprüft, ob das Ende des Körpers möglicherweise erreichbar ist. Wenn ja, wird a eingefügtreturn 0;
quelle
main
Selbst in C ++ gibt es KEINE Überladung . Hauptfunktion ist der Einstiegspunkt für ein Programm und es sollte nur eine einzige Definition existieren.Für Standard C.
Für Standard-C ++:
Der C ++ - Standard sagt ausdrücklich "Es [die Hauptfunktion] muss einen Rückgabetyp vom Typ int haben, aber ansonsten ist sein Typ implementierungsdefiniert" und erfordert dieselben zwei Signaturen wie der C-Standard.
In einer gehosteten Umgebung (AC-Umgebung, die auch die C-Bibliotheken unterstützt) ruft das Betriebssystem auf
main
.In einer nicht gehosteten Umgebung (eine für eingebettete Anwendungen vorgesehene) können Sie den Einstiegspunkt (oder den Ausgang) Ihres Programms jederzeit mithilfe der Anweisungen des Vorprozessors wie ändern
Wobei Priorität eine optionale ganzzahlige Zahl ist.
Der Pragma-Start führt die Funktion vor der Hauptfunktion aus (vorrangig), und der Pragma-Exit führt die Funktion nach der Hauptfunktion aus. Wenn es mehr als eine Startanweisung gibt, entscheidet die Priorität, welche zuerst ausgeführt wird.
quelle
Eine Überlastung ist nicht erforderlich. Ja, es gibt 2 Versionen, aber es kann jeweils nur eine verwendet werden.
quelle
Dies ist eine der seltsamen Asymmetrien und Sonderregeln der C- und C ++ - Sprache.
Meiner Meinung nach existiert es nur aus historischen Gründen und es steckt keine wirklich ernsthafte Logik dahinter. Beachten Sie, dass dies
main
auch aus anderen Gründen besonders ist (z. B. kannmain
C ++ nicht rekursiv sein und Sie können seine Adresse nicht übernehmen, und in C99 / C ++ dürfen Sie eine endgültigereturn
Anweisung weglassen ).Beachten Sie auch, dass es auch in C ++ keine Überladung ist ... entweder hat ein Programm die erste Form oder es hat die zweite Form; es kann nicht beides haben.
quelle
return
Anweisung auch in C weglassen (seit C99).main()
und die Adresse übernehmen. C ++ wendet Grenzwerte an, die C nicht hat.argc
beim Rekursieren keinen negativen Wert an den Standard IIUC übergeben können (5.1.2.2.1 gibt keine Einschränkungen für anargc
undargv
gelten nur für den ersten Anruf beimain
).Was ungewöhnlich
main
ist, ist nicht, dass es auf mehr als eine Weise definiert werden kann, sondern dass es nur auf eine von zwei verschiedenen Arten definiert werden kann.main
ist eine benutzerdefinierte Funktion; Die Implementierung deklariert keinen Prototyp dafür.Das Gleiche gilt für
foo
oderbar
, aber Sie können Funktionen mit diesen Namen beliebig definieren.Der Unterschied besteht darin, dass
main
die Implementierung (die Laufzeitumgebung) nicht nur Ihren eigenen Code aufruft. Die Implementierung ist nicht auf die gewöhnliche Semantik von C-Funktionsaufrufen beschränkt, daher kann (und muss) sie mit einigen Variationen umgehen - es ist jedoch nicht erforderlich, unendlich viele Möglichkeiten zu handhaben. Dasint main(int argc, char *argv[])
Formular ermöglicht Befehlszeilenargumente und istint main(void)
in C oderint main()
C ++ nur eine Annehmlichkeit für einfache Programme, die keine Befehlszeilenargumente verarbeiten müssen.Wie der Compiler damit umgeht, hängt von der Implementierung ab. Die meisten Systeme haben wahrscheinlich Aufrufkonventionen, die die beiden Formulare effektiv kompatibel machen, und alle Argumente, die an eine
main
Definition ohne Parameter übergeben werden, werden stillschweigend ignoriert. Wenn nicht, wäre es für einen Compiler oder Linker nicht schwierig,main
speziell zu behandeln . Wenn Sie neugierig sind, wie es auf Ihrem System funktioniert , sehen Sie sich möglicherweise einige Baugruppenlisten an.Und wie viele Dinge in C und C ++ sind die Details größtenteils ein Ergebnis der Geschichte und willkürlicher Entscheidungen, die von den Designern der Sprachen und ihren Vorgängern getroffen wurden.
Beachten Sie, dass sowohl C als auch C ++ andere implementierungsdefinierte Definitionen für zulassen
main
- aber es gibt selten einen guten Grund, sie zu verwenden. Bei freistehenden Implementierungen (z. B. eingebetteten Systemen ohne Betriebssystem) ist der Programmeinstiegspunkt implementierungsdefiniert und wird nicht unbedingt aufgerufenmain
.quelle
Dies
main
ist nur ein Name für eine vom Linker festgelegte Startadresse, wobeimain
der Standardname ist. Alle Funktionsnamen in einem Programm sind Startadressen, an denen die Funktion startet.Die Funktionsargumente werden auf den Stapel geschoben / verschoben. Wenn für die Funktion keine Argumente angegeben sind, werden keine Argumente auf den Stapel geschoben / verschoben. So kann main sowohl mit als auch ohne Argumente arbeiten.
quelle
Dabei speichert die Variable argc die Anzahl der übergebenen Daten und argv ist ein Array von Zeigern auf char, die auf die von der Konsole übergebenen Werte verweisen. Ansonsten ist es immer gut zu gehen
In jedem Fall kann es jedoch nur ein main () in einem Programm geben, da dies der einzige Punkt ist, an dem ein Programm seine Ausführung beginnt und daher nicht mehr als eins sein kann. (hoffe es ist würdig)
quelle
Eine ähnliche Frage wurde bereits gestellt: Warum wird eine Funktion ohne Parameter (im Vergleich zur tatsächlichen Funktionsdefinition) kompiliert?
Eine der am besten bewerteten Antworten war:
Also, ich denke es ist wie
main
deklariert wird (wenn Sie den Begriff "deklariert" anwenden könnenmain
). In der Tat können Sie so etwas schreiben:und es wird immer noch kompiliert und ausgeführt.
quelle
main
, da es ein Problem gibt, das noch nicht erwähnt wurde: noch mehr Argumente fürmain
! "Unix (aber nicht Posix.1) und Microsoft Windows" fügen hinzuchar **envp
(ich erinnere mich, dass DOS das auch erlaubt hat, nicht wahr ?), Und Mac OS X und Darwin fügen noch einen weiteren char * Zeiger für "willkürliche vom Betriebssystem bereitgestellte Informationen" hinzu. wikipediaSie müssen dies nicht überschreiben, da jeweils nur eine verwendet wird. Ja, es gibt zwei verschiedene Versionen der Hauptfunktion
quelle