Warum kann main () in C ++ nicht inliniert werden?


69

Ich habe die C ++ - FAQs gelesen und einen Satz bemerkt.

main () kann nicht inline sein.

Warum ist das?


56
Interessantere Frage für mich: Warum möchte jemand versuchen, sie zu integrieren?
RiaD

7
Um Ihren Main in OS-Kernel-Code zu integrieren? :)
Mehran

21
Das ist doch albern, oder? Beim Inlining wird der Inhalt einer Methode direkt in den aufrufenden Code eingefügt, anstatt eine separate Methode zu sein. Das bedeutet, dass Sie Ihr Betriebssystem neu kompilieren müssen, damit Ihre mainFunktion darin kompiliert wird. Die Antwort lautet also, weil Sie Ihr Betriebssystem nicht neu kompilieren können.
Kieren Johnstone

3
@Kieren: Deshalb möchten Sie die Funktion nie physisch inline setzen. Es ist nicht genau das Gleiche, warum die Funktion nicht markiert werden soll inline(was nur ein Hinweis ist!).
Leichtigkeitsrennen im Orbit

5
Dies in eine C ++ - FAQ zu schreiben, kommt mir irgendwie albern vor, denn warum sollten Sie das tun? Das ist wie wenn Sie ein Warnschild für eine Situation sehen, die keinen Sinn ergibt.
Schockieren

Antworten:


105

In C ++ ist es nicht legal, die Hauptfunktion in Ihrem Code aufzurufen, daher kann sie niemals eingebunden werden.


17
Dies ist der Grund, denken Sie darüber nach.
Tamás Szelei

17
@iammilind: *static_cast<int*>(0) = 10Kompiliert auch, und das bedeutet nicht, dass es korrekt ist ... wie es bei jeder ODR-Verletzung und so vielen anderen Dingen der Fall ist ... die Tatsache, dass es kompiliert, bedeutet nicht, dass es ein legales Programm ist.
David Rodríguez - Dribeas

5
@iammilind: Die Anweisung "es kompiliert" erfordert Kontext. Weil das Kompilieren nach dem Standard sicherlich nicht erforderlich ist und es tatsächlich nicht in allen Fällen kompiliert.
Benjamin Lindley

4
seufz für alle, die sich fragen, op fragte in einem Kommentar "aus einem Grund", dann kommentierte ich ihm zu antworten, aber er löschte seinen. Nicht cool, op.
Tamás Szelei

4
@ sepp2k: siehe meine Antwort. Kurz gesagt, das Inlining von Maschinencode ist für die Frage irrelevant, aber technisch kann es auf zwei verschiedene Arten in den Aufruf aus der Laufzeitbibliothek eingefügt werden. es wird jedoch nicht gemacht, da es keinen Vorteil gibt. :-)
Prost und hth. - Alf

66

Weil der Standard dies sagt:

[2003: 3.6.1/3]: Die Funktion main darf nicht innerhalb eines Programms verwendet werden (3.2). Die Verknüpfung (3.5) von main ist implementierungsdefiniert. Ein Programm, das main als inline oder statisch deklariert, ist fehlerhaft. Der Name main ist nicht anderweitig reserviert. [Beispiel: Elementfunktionen, Klassen und Aufzählungen können als main bezeichnet werden, ebenso wie Entitäten in anderen Namespaces. ]]

Und warum steht es so? Weil es versucht, so viel wie möglich über die Implementierung maindem Einzelnen zu überlassen ... nun, Implementierung ... und die Implementierungen nicht einschränken möchte, indem es verlangt, dass inlinedies hier gültig ist, wenn es wohl keinen praktischen Nutzen hat.


Mein Freund im Ausschuss bestätigte dies:

Es gibt keinen Grund, warum ein inline main()per se nicht funktionieren würde. [..] Ich könnte einen C ++ - Interpreter haben, der Inline aufrufen kann main(). [..] [Aber] inline/ static main()sind verboten, um hoffentlich Verwirrung zu vermeiden. Es fällt mir schwer, mir vorzustellen, dass die Begründung etwas Zusätzliches zu dem ist, was bereits in [diesen Fragen und Antworten] gesagt wurde.


Übrigens, verwechseln Sie das inlineHinweis-Schlüsselwort nicht mit tatsächlichen Inlining-Funktionen. Sie können eine Funktion markieren inlineund sie ist möglicherweise nicht physisch inline.

Selbst wenn es wahr wäre, dass main"nicht inliniert werden kann" (und genau genommen ist es nicht wahr, obwohl Inlining mainziemlich umständlich und sinnlos wäre, wie in anderen Antworten erläutert), könnte es theoretisch das inlineHinweis-Schlüsselwort gut unterstützen.

Aus dem oben genannten Grund und in der Antwort von litb: Es würde die Sache ohne wirklichen Nutzen komplizieren.


4
+1 für die Angabe des Standards. Dies kann jedoch die Frage des OP möglicherweise nicht vollständig beantworten. Bisher habe ich keine berechtigte Antwort dagegen gesehen, außer Ihrem Beitrag.
Thomas Matthews

@Thomas: Die Begründung, die ich gegeben habe, ist ziemlich dieselbe wie die in den anderen Antworten, nur mit weniger Details, warum es möglicherweise keinen praktischen Nutzen gibt. :)
Leichtigkeitsrennen im Orbit

re "will Implementierungen nicht einschränken, indem verlangt wird, dass Inline gültig ist", die Unterstützung inlinefür mainist trivial, da sie einfach ignoriert werden kann, was keine Implementierung einschränkt, weshalb dieser mögliche Grund für das Verbot des Standards kein Wasser enthält. Es tut uns leid. Aber ich habe nicht mehr zu bieten als in meiner Antwort, dass es keinen Sinn macht inline(und darüber sind wir uns einig, denke ich).
Prost und hth. - Alf

@ Cheersandhth.-Alf: Es würde bedeuten, dass Sie mainin mehreren TUs definieren können, ob die Definitionen (unter anderem) alle lexikalisch identisch sind, was nur so wenig Sinn macht, dass es sich lohnt, es zu verbieten.
Leichtigkeitsrennen im Orbit

2
@meet: Warum sollte es nicht sein? Im Gegensatz zu anderen Funktionen, die der Benutzer definiert, mainhat dies eine Bedeutung, die mit der Laufzeit der Implementierung und mit dem Host-Betriebssystem interagieren muss (da dies der Programmeinstiegspunkt ist). Daher ist es für ein Komitee von Personen nicht sinnvoll, zu viel darüber zu schreiben. Daran erinnert , dass die Verknüpfung von anderen Funktionen benutzerdefiniert ist so in der Tat, der Standard ist beschränke mainleicht hier, mit den Worten „zu Ihrem Compiler Anbieter zu hören , weil sie dies wählen bekommen Sie nicht“. :)
Leichtigkeitsrennen im Orbit

27

Die C-Laufzeitbibliothek muss dieses Symbol finden, um zu "wissen", welche Funktion ausgeführt werden soll.


Bedeutet dies, dass Linker die Symbole für andere Inline-Funktionen nicht finden können?
Thomas Matthews

@ Thomas Matthews: Kommt darauf an, wie schlau der Linker ist. Im Allgemeinen sind sich Linker der Inline-Funktionen nicht bewusst. Sie haben eine interne Verknüpfung. Moderne Linker sind etwas schlauer, da sie versuchen, ganze Programmoptimierungen durchzuführen, und das ist ein ganz anderes Ballspiel. :)
Billy ONeal

Ich würde auch sagen , dass in C - Laufzeit gibt ( in der Regel) ein explizites ist callauf die main()Funktion, und es fast immer dynamisch verbunden ist. In einem typischen Fall kann es also auf keinen Fall funktionieren .
Whitequark

17

Sie können main () nicht direkt aufrufen (dies ist in c ++ verboten), daher macht es keinen Sinn, es einzubinden.


5
"Es hat keinen Sinn" ist kein ausreichender Grund, etwas völlig zu verbieten. Es steckt [etwas] mehr dahinter.
Leichtigkeitsrennen im Orbit

14

Wird normalerweise main()von der init()Systemfunktion aufgerufen . Daher ist es erforderlich, dass es genau eine Definition für geben kann main().

Wenn wir nun inlinedie main()Funktion können und in eine Header-Datei aufnehmen können, gibt es für jede Übersetzungseinheit eine andere Definition für main(). Welches ist nicht erlaubt. Sie können main()in a namespaceund inlinees deklarieren . Aber nicht das Globale main().


Sie können dies auch ohne tun inline.
Leichtigkeitsrennen im Orbit

@Tomalak, dann führt dies zu einem Mehrfachdefinitionsfehler. Ist es nicht?
Iammilind

Nicht, wenn jede Definition eine interne Verknüpfung hat. (Beachten Sie, dass dies auch der Grund ist, warum static int main()es schlecht geformt ist: D)
Leichtigkeitsrennen im Orbit

@ Tomlak, ja. static int main()ist äquivalent zu namespace { int main() }. Was ich in der Antwort behandelt habe.
Iammilind

Man kann auch eine Funktion inline und nur eine Instanz davon haben. Compiler und Linker können mehrere Instanzen der mainFunktion identifizieren. Worum geht es also?
Thomas Matthews

10

Zunächst müssen Sie verstehen, wie die Arbeit mit Inline funktioniert

Beispiel:

 inline void f() {
     int a  = 3;
     a += 3;
     cout << a;
 }

 int main() {
      f();
      return 0;
 }

wird für den Compiler wie folgt aussehen:

 int main() {
        int a  = 3;
        a += 3;
        cout << a;
        return 0;
 }

Wie möchten Sie in diesem Beispiel die Hauptinline erstellen? Diese Methode ist sofort inline.


@the_drow: Ich hatte gehofft, dass Nirmus dies sehen und selbst über das Update nachdenken muss! Trotzdem danke!
Leichtigkeitsrennen im Orbit

2
Was ist also der Unterschied zwischen der Behandlung einer inlined-Funktion mit nur einem Aufruf und der mainFunktion mit nur einem Aufruf?
Thomas Matthews

This method is inline immediately.Nicht wahr. inlineist nur ein Hinweis . Es führt kein Inlining von Funktionen durch.
Leichtigkeitsrennen im Orbit

7

Der C ++ - Standard besagt, dass die mainFunktion gemäß der Antwort von @Tomalak Geret'kal nicht eingebunden werden kann. Diese Antwort diskutiert die Möglichkeit des Inlining der mainFunktion, wenn die Einschränkung im Standard aufgehoben wurde.

Definition von Inline
Das inlineSchlüsselwort ist ein Vorschlag an den Compiler, den Inhalt der Funktion vor Ort einzufügen. Eine Absicht besteht darin, den Overhead zu beseitigen, der beim Aufrufen und Zurückkehren von einer Funktion (Unterroutine) vorhanden ist.

Eine wichtige Situation beim Inlining ist der Fall, in dem ein Zeiger auf die Funktion vorhanden ist. In diesem Fall muss mindestens eine statische Kopie der Funktion vorhanden sein. In diesem Fall kann der Linker "externe Verknüpfungen" der Inline-Funktion auflösen, da es eine statische Version gibt.

Es ist wichtig zu beachten, dass der Compiler und der Linker bestimmen, ob der Inhalt eingefügt oder eine einzelne Instanz der Funktion aufgerufen werden soll.

Bemerkenswert sind auch Funktionen, die vom Programmierer nicht markiert werden auch vom Compiler eingebunden werden können.

Die Hauptfunktion inlining
Da es nur ein Aufruf ist mainerlaubt, wie es verbunden ist , ist an den Compiler auf. Einzelne Instanzen von Inline-Funktionen sind im Standard zulässig. Der Compiler darf eine inlinedFunktion in einen Funktionsaufruf in eine einzelne Instanz konvertieren . Der Compiler würde also einen Inline-Vorschlag für die Funktion ignorierenmain .

Der Compiler und der Linker müssten sicherstellen, dass nur eine Instanz der Inline- mainFunktion vorhanden ist. Hier kommt der schwierige Teil ins Spiel, insbesondere bei der externen Verknüpfung. Ein Prozess, um sicherzustellen, dass eine Instanz vorhanden ist, besteht darin, Informationen zu hinterlassen, dass eine Übersetzung eine Hauptfunktion hat, unabhängig davon, ob sie inline ist oder nicht. Hinweis: Wenn eine Inline-Funktion aufgerufen wird, kann der Compiler die Funktion aus den Symboltabellen für die externe Verknüpfung entfernen, da die Funktion nicht von externen Funktionen aufgerufen wird.

Zusammenfassung
Technisch gesehen hindert nichts die mainInline-Funktion der Funktion. Die Maschinerie existiert bereits zum Konvertieren von Inline-Funktionen in einzelne Instanzen und zum Identifizieren mehrerer Instanzen einer Funktion. Wenn es einen Zeiger auf eine inline-Funktion gibt, wird eine einzelne Instanz einer Funktion erstellt, sodass sie eine Adresse hat. Diese Maschinerie würde die Anforderungen der Laufzeitbibliothek erfüllen, um maineine Adresse zu haben. Im Fall von inlinefür die mainFunktion würde sie ignoriert, aber es sollte keinen Grund geben, diese Syntax zu verhindern (außer verwirrende Personen). Schließlich gibt es bereits redundante Syntaxfälle, z. B. das Deklarieren eines Parameters, der als Wert (Kopie) als übergeben wird const.

"Das ist nur meine Meinung, ich könnte mich irren." - Dennis Miller, Komiker.


7

Andere haben bemerkt, dass eine Anrufung von main auf der Ebene des Maschinencodes nicht sinnvoll eingefügt werden kann. Das ist Müll. Es würde ein bisschen Hilfe vom Linker erfordern (wie die globale Optimierung) oder eine Neukompilierung eines Teils der Laufzeitbibliothek pro Anwendung, aber es ist durchaus machbar, hier kein technisches Problem.

Doch das Hinting des Effekt inline, dass Anrufe vorzugsweise inlined werden sollen, ist irrelevant für eine Funktion , die nur einmal und auf der obersten Ebene der Kontrolle genannt wird, alsmain ist.

Die einzige garantierte Wirkung voninline ist eine externe Verknüpfung Funktion zu ermöglichen , definiert werden (identisch) in zwei oder mehr Übersetzungseinheiten, dh Auswirkungen auf die One Definition Rule.

In der Praxis ermöglicht dies das Platzieren der Definition in einer Header-Datei, und das Platzieren in einer Header-Datei ist auch praktisch erforderlich, um identische Definitionen zu gewährleisten.

Das macht Sinn nicht main, so gibt es keinen Grund für mainsein inline.


1
"Es gibt keinen Grund mainzu sein inline" ist zwingend, aber keine direkte Erklärung dafür, warum es so gemacht wurde, dass es nicht markiert werden kann inline.
Leichtigkeitsrennen im Orbit

Ich habe dasselbe in meiner Antwort erklärt. Ihre ist jedoch aufwändiger.
Iammilind

Nun, ich denke, der Standard gibt sich nicht die Mühe, Dinge zu unterstützen, die niemand jemals benutzen wird. Aber ich denke außerdem ist die Beschreibung von mainnicht perfekt. Zum Beispiel habe ich immer gedacht und denke immer noch, dass das Bit "nach der ersten Aussage von main" grundlegend falsch ist. Aber ich habe noch nie darüber gesprochen. Vielleicht ist es nur mein unvollkommenes Verständnis von Englisch ...
Prost und hth. - Alf

1
@anonymous downvoters: Bitte erläutern Sie Ihre Gründe für das Downvoting, damit andere von Ihren Erkenntnissen profitieren können (he he).
Prost und hth. - Alf

@Alf: In ähnlicher Weise bemüht sich der Standard nicht, Dinge zu verbieten, es sei denn, es gibt einen richtigen Grund dafür. :)
Leichtigkeitsrennen im Orbit

6

Sie können nur maineinmal definieren . Putten inlinewürde also keinen Zweck erfüllen - hat inlinenur einen wesentlichen Zweck für Funktionen, die Sie mehrmals in einem Programm definieren können (alle Definitionen werden so behandelt, als ob es nur eine Definition gäbe und alle Definitionen müssen gleich sein).

Da inlineFunktionen in einem Programm mehrfach definiert werden können und inlineauch dazu dienen, eine inlinemarkierte Funktion so schnell wie möglich aufzurufen, verlangt der Standard, dass inlineFunktionen in jeder Übersetzungseinheit definiert werden, in der sie verwendet wird. Daher werfen Compiler normalerweise die Definition einer Funktion weg, wenn dies der Fall ist inlineund die Funktion vom Code in der aktuellen Übersetzungseinheit nicht verwendet wurde. Das zu tun mainwäre völlig falsch, was zeigt, dass inlineund die Semantik mainvöllig inkompatibel ist.

Beachten Sie, dass die Frage in Ihrem Titel "Warum kann main () in C ++ nicht eingefügt werden?" und die Aussage, die Sie aus dem Standard zitieren, betrifft verschiedene Dinge. Sie fragen, ob die Funktion inline sein kann, was üblicherweise so verstanden wird, dass der Code einer aufgerufenen Funktion ganz oder teilweise in die aufrufende Funktion eingefügt wird. Nur eine Funktion zu markieren inlinebedeutet nicht, diese Funktion überhaupt zu inlinieren. Es ist ganz und gar die Entscheidung des Compilers, und wenn Sie nie aufrufen main(und dies auch nicht können), gibt es natürlich nichts zu tun.


1
Die Terminologie im Standard ist etwas umständlich, aber obwohl eine Inline-Funktion mehrfach definiert werden kann, müssen alle Definitionen identisch sein und das Verhalten des Codes muss so sein, als ob es nur einmal definiert worden wäre. (Die Tatsache, dass eine Inline-Funktion in jeder Übersetzungseinheit definiert werden muss, die sie verwendet, ist etwas problematischer. Die einzige Übersetzungseinheit, die sie verwendet, ist eine, die Sie nicht geschrieben haben und die bereits mit Ihrem System kompiliert wurde.)
James Kanze

2
@ James: Re die Bemerkung in Klammern, ja, aber die Implementierung darf jede Magie machen, die sie will. <g>
Prost und hth. - Alf

@Alf Einverstanden, solange das beobachtbare Verhalten erhalten bleibt. Aber der Standard erfordert keine solche Magie; so dass mainInline erfordern würde , es sein. In der Vergangenheit brauchte C ++ keine Magie. (Aber das war vor Vorlagen.)
James Kanze

3

Wenn Sie statisch mit der CRT verknüpft sind und ein Inlining zur Kompilierung der Verknüpfungszeit aktiviert haben (wie dies bei MSVC der Fall ist), ist es möglicherweise möglich, diese zu integrieren.

Aber es macht nicht wirklich Sinn. Es wird aufgerufen , sobald und dass Funktionsaufruf-Overhead praktisch Null auf alles verglichen wird sonst noch vor der ersten Zeile in Haupt ausführt erfolgt.

...

Aaand, es ist eine einfache Möglichkeit, das Symbol nur einmal in Ihrer ausführbaren Datei erscheinen zu lassen. :) :)


2

Es gibt eine Reihe grundlegender Gründe. Grundsätzlich mainwird von der Basisinitialisierungsroutine der Laufzeit aufgerufen und nur von dort. Dieser Code wurde (offensichtlich) kompiliert, ohne zu wissen, dass Ihr Code maininline war. Moderne Compilertechnologie ist in der Lage, Modulgrenzen zu überschreiten, ist jedoch eine erweiterte Funktion, die von vielen älteren Compilern nicht unterstützt wird. Und natürlich sind die Vorteile von Inlining nur dann vorhanden, wenn eine Funktion sehr häufig aufgerufen wird. per definitionem main wird genau einmal aufgerufen, nicht mehr und nicht weniger.


2

Ich sehe, dass der Standard dies sagt, aber die wirkliche praktische Antwort wäre so einfach wie die Aussage, dass die jedem C- und C ++ - Programm hinzugefügte Laufzeit irgendwann in der ausführbaren Datei aufgerufen werden muss. Diese Funktion sollte ein externes Symbol (und eine Adresse beim Ausführen) haben, damit der Linker feststellen kann, dass sie zu Beginn der Ausführung aufgerufen wird. Daher können Sie es nicht als deklarieren inline, da der Compiler inline kein externes Symbol dafür generieren würde.


Das Markieren einer Funktion inlineführt nicht unbedingt dazu, dass die Funktion inline wird.
Leichtigkeitsrennen im Orbit

Eine inlineFunktion verfügt über eine externe Verknüpfung, es sei denn, es handelt sich um eine explizit deklarierte Namespace-Bereichsfunktion static.
Prost und hth. - Alf

1

Da es die main () -Funktion ist, die die Ausführung startet, ist alles in sich main()selbst , wenn der Code in Binär kompiliert wird . man kann also sagen, es ist bereits inline!

Und ja, es ist illegal, Inline für Ihr C ++ - Programm zu verwenden, das ist mehr eine Syntax!


1

Für die meisten Kombinationen von Compiler / Archetecture ist die main() Funktion in der Quelle zu einer einigermaßen normalen Funktion in der endgültigen Binärdatei. Dies liegt nur daran, dass es für diese Archetekturen praktisch ist, nicht daran, dass der Standard dies vorschreibt.

Bei speicherbeschränkten Archetectures optimieren viele Compiler, die anstelle eines dynamischen Linker-freundlichen Containers (wie elf oder xcoff) eine flache Binärdatei (wie das Intex-Hex-Format) erzeugen, die gesamte Boilerplate, da sie nur aufgebläht wäre. Einige Architekturen unterstützen Funktionsaufrufe überhaupt nicht (auf diesen Plattformen ist nur eine begrenzte Teilmenge von C ++ möglich.)

Um die unterschiedlichsten Architekturen und Build-Umgebungen zu unterstützen, behalten die Standardwahlen die Semantik von bei main() so offen wie möglich, damit der Compiler das tun kann, was für die unterschiedlichsten Plattformen richtig ist. Das bedeutet, dass viele in der gesamten Sprache verfügbare Funktionen nicht auf das Starten und Herunterfahren der Anwendung selbst angewendet werden können.

Wenn Sie so etwas wie eine Inline-Funktion main()(oder einen Wiedereintritt oder eine ausgefallene Funktion) benötigen, können Sie die Hauptfunktion natürlich etwas anderes nennen:

inline int myMain(int argc, char **argv) { /* whatever */ }
int main(int argc, char **argv) { return myMain(argc, argv); }

1

Inline-Funktionen haben standardmäßig einen statischen Gültigkeitsbereich. Wenn wir main () als Inline deklarieren, ist der Gültigkeitsbereich auf die Datei beschränkt, in der er definiert ist. Die C-Startbibliothek (vom Compilerhersteller bereitgestellt) benötigt jedoch 'main' als globales Symbol. Es gibt einige Compiler, mit denen die Einstiegspunktfunktion (z. B. main) mithilfe von Linker-Flags geändert werden kann.


0

Inline-Funktionen haben normalerweise keine Adresse, daher gibt es keine tragbare Möglichkeit, main aufzurufen. main () benötigt eine Adresse, in die der Init-Code springen kann. Inline-Funktionen sollen in der aufrufenden Funktion stecken bleiben. Wenn main inline ist, sollte es in den Init-Code des Programms eingebunden werden, der ebenfalls nicht portierbar ist.


Es gibt keine tragbare Möglichkeit, main in einem C ++ - Programm aufzurufen, auch wenn dies nicht der Fall ist inline.
Flexo

-2

Betriebssystem lädt Binärdaten in den Speicher; sucht nach Einstiegspunkt (das 'Hauptsymbol' in c / c ++); macht einen weiten Sprung zu den Adressen des Einstiegspunkt-Labels. Das Betriebssystem weiß nichts über die Hauptfunktion in Ihrem Code, bis das Programm nicht geladen ist.


8
Auf den meisten oder wahrscheinlich allen Systemen ist nicht das Betriebssystem für den Anruf verantwortlich main. Stattdessen ruft das Betriebssystem den Einstiegspunkt auf Maschinencodeebene für das Programm auf. Für C und C ++ ist dieser Einstiegspunkt normalerweise eine Funktion in der Laufzeitbibliothek, die wiederum verschiedene Initialisierungsaufgaben ausführt, dann aufruft mainund schließlich bereinigt (z. B. installierte Exit-Handler aufruft) und beendet.
Prost und hth. - Alf
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.