Je lisais la FAQ C ++ et j’ai remarqué une phrase.
main () ne peut pas être en ligne.
Pourquoi est-ce?
En C ++, il n’est pas légal d’appeler la fonction principale dans votre code, il n’y aurait donc aucun moyen de l’inclure.
Parce que la norme le dit:
[2003: 3.6.1/3]
: la fonction main ne doit pas être utilisée (3.2) dans un programme. Le lien (3.5) de main est défini par la mise en œuvre. Un programme qui déclare principal être en ligne ou statique est mal formé. Le nom principal n’est pas autrement réservé. [Exemple: les fonctions membres, les classes et les énumérations peuvent être appelées main, tout comme les entités des autres espaces de noms. ]
Et pourquoi dit-il ça? Parce que cela essaye de laisser autant de choses sur l’implémentation de main
à l’individu .. bien, l’ implémentation .. comme c’est possible, et ne veut pas limiter les implémentations en exigeant inline
soit valide ici sans aucun avantage pratique.
Mon ami au comité a confirmé ceci:
Il n’y a aucune raison pour qu’une
main()
ne fonctionne pas en soi. [..] Je pourrais avoir un interpréteur C ++ capable d’invoquer mainlinemain()
. [..] [Mais]inline
/static
main()
sont interdits afin d’éviter toute confusion. J’ai du mal à imaginer que la justification serait quelque chose en plus de ce qui a déjà été dit dans [cette Q & R].
BTW, ne confondez pas le mot-clé de conseil en inline
avec les fonctions en cours. Vous pouvez marquer une fonction en inline
et celle-ci peut ne pas être physiquement intégrée.
Donc, même s’il était vrai que main
“ne peut pas être incorporé” (et à proprement parler ce n’est pas vrai, bien que l’inlining main
soit plutôt maladroit et inutile, comme expliqué dans d’autres réponses), .
Ce n’est pas pour la raison indiquée ci-dessus, et dans la réponse de Litb: cela compliquerait les choses sans réel avantage.
La bibliothèque d’exécution C doit trouver ce symbole afin de “savoir” quelle fonction exécuter.
Vous ne pouvez pas appeler directement main () (c’est interdit dans c ++), il est donc inutile de l’inclure.
En général, main()
est appelée à partir de la fonction système init()
. Il est donc nécessaire qu’il y ait exactement une définition pour main()
.
Maintenant, si nous pouvons intégrer la fonction main()
et inclure dans un fichier d’en-tête, pour chaque unité de traduction, la définition de main()
sera différente. Ce qui n’est pas autorisé Vous pouvez déclarer main()
dans un namespace
et l’ inline
. Mais pas le main()
global main()
.
Tout d’abord, vous devez comprendre comment fonctionne le travail avec inline
Exemple:
inline void f() { int a = 3; a += 3; cout << a; } int main() { f(); return 0; }
ressemblera au compilateur en tant que:
int main() { int a = 3; a += 3; cout << a; return 0; }
En regardant cet exemple, comment voulez-vous que le principal soit en ligne? Cette méthode est en ligne immédiatement.
Le standard C ++ indique que la fonction main
ne peut pas être intégrée, par la réponse de @Tomalak Geret’kal. Cette réponse discute de la possibilité d’inclure la fonction main
si la ressortingction de la norme était supprimée.
Définition de Inline
Le mot-clé inline
est une suggestion au compilateur pour coller le contenu de la fonction in-situ. Une des intentions est de supprimer la surcharge présente lors de l’appel et du retour d’une fonction (sous-routine).
Une situation importante de l’inlining est le cas où il y a un pointeur sur la fonction. Dans ce cas, il doit y avoir au moins une copie statique de la fonction. Dans ce cas, l’éditeur de liens peut résoudre les “liens externes” de la fonction intégrée car il existe une version statique.
Important de noter que le compilateur et l’éditeur de liens déterminent s’il faut ou non coller le contenu ou appeler une seule instance de la fonction.
De même, les fonctions qui ne sont pas étiquetées par le programmeur peuvent également être intégrées par le compilateur.
Insertion de la fonction principale
Comme il n’y a qu’une seule invocation de main
autorisée, la manière dont elle est liée revient au compilateur. Des instances uniques de fonctions en ligne sont autorisées par la norme. Le compilateur est autorisé à convertir une fonction intégrée en un appel de fonction à une seule instance. Le compilateur ignorera donc une suggestion en ligne pour la fonction main
.
Le compilateur et l’éditeur de liens doivent s’assurer qu’une seule instance de la fonction main
intégrée existe. C’est là que la partie délicate entre en jeu, en particulier avec les liaisons externes. Un processus permettant de s’assurer qu’une instance est de laisser une information indiquant qu’une traduction a une fonction «principale», qu’elle soit ou non en ligne. Remarque: Lorsqu’un appel à une fonction en ligne est effectué, le compilateur est autorisé à supprimer la fonction des tables de symboles pour la liaison externe, car l’idée est que la fonction ne sera pas appelée par des fonctions externes.
Résumé
Techniquement , rien n’empêche la fonction main
d’être mise en ligne. La machine existe déjà pour convertir des fonctions intégrées en instances uniques et pour identifier plusieurs instances d’une fonction. Lorsqu’il existe un pointeur sur une fonction intégrée, une seule instance d’une fonction est créée, elle a donc une adresse. Cette machine satisferait aux exigences de la bibliothèque d’exécution pour le main
ayant une adresse. Dans le cas de la fonction inline
pour la fonction main
, celle-ci serait ignorée, mais il ne devrait y avoir aucune raison d’empêcher cette syntaxe (sauf les personnes déroutantes). Après tout, il existe déjà des cas de syntaxe redondants, tels que la déclaration d’un paramètre transmis par valeur (copie) en tant que const
.
“C’est juste mon avis, je peux me tromper.” – Dennis Miller, comédien.
D’autres ont fait remarquer qu’une invocation de la ligne main
ne pouvait être signifiée de manière significative au niveau du code machine. C’est n’importe quoi. Cela nécessiterait un peu d’aide de l’éditeur de liens (comme l’optimisation globale) ou une recompilation par application d’un peu de la bibliothèque d’exécution, mais c’est tout à fait faisable, pas de problème technique ici.
Cependant, l’effet d’ inline
d’ inline
, à savoir que les appels doivent de préférence être intégrés, n’est pas pertinent pour une fonction qui n’est appelée qu’une seule fois et au plus haut niveau de contrôle, comme main
est le main
.
Le seul effet garanti de la commande en inline
est de permettre à une fonction de liaison externe d’être définie (de manière identique) dans deux unités de traduction ou plus, c’est-à-dire affectant la règle de définition unique.
En pratique, cela permet de placer la définition dans un fichier d’en-tête et de la placer dans un fichier d’en-tête, ce qui est pratiquement nécessaire pour garantir des définitions identiques.
Cela n’a aucun sens pour main
, il n’y a donc aucune raison pour que main
soit en inline
.
Vous ne pouvez définir le main
qu’une seule fois. Donc, mettre en inline
ne servirait à rien – inline
uniquement a un rôle important sur les fonctions que vous pouvez définir plusieurs fois dans un programme (toutes les définitions seront traitées comme si il n’y avait qu’une seule définition et toutes les définitions doivent être identiques).
Étant donné que inline
fonctions en inline
peuvent être définies plusieurs fois dans un programme et inline
sert également à passer des appels à une fonction marquée en inline
aussi rapidement que possible, la norme exige que inline
fonctions en inline
soient définies dans chaque unité de traduction utilisée. Les compilateurs jettent donc généralement la définition d’une fonction si celle-ci est intégrée et la fonction n’a pas été utilisée par le code dans l’unité de traduction actuelle. Faire cela pour main
serait totalement faux, ce qui montre que inline
et la sémantique main
est totalement incompatible.
Notez que la question dans votre titre “Pourquoi main () en C ++ ne peut pas être inséré?” et la déclaration que vous citez de la norme concerne différentes choses. Vous demandez si la fonction peut être intégrée, ce qui est généralement compris pour insérer complètement ou partiellement le code d’une fonction appelée dans la fonction appelante. Le simple fait de marquer une fonction en inline
ne signifie pas que la fonction doit être insérée. C’est entièrement la décision du compilateur, et bien sûr, si vous n’appelez jamais main
(et que vous ne pouvez pas le faire), alors il n’y a rien à inclure.
Si vous avez lié statiquement au CRT et activé une compilation-inlining (comme MSVC) au moment de la liaison, il peut être possible de l’inclure.
Mais cela n’a pas vraiment de sens. Il sera appelé une fois et cette fonction d’appel-overhead est pratiquement nulle comparée à tout ce qui est fait avant la première ligne de l’exécution principale.
…
C’est un moyen facile de forcer le symbole à apparaître une seule fois dans votre exécutable. 🙂
Il y a un certain nombre de raisons fondamentales. Fondamentalement, main
est appelée à partir de la routine d’initialisation de base du moteur d’exécution, et uniquement à partir de là. Ce code était (évidemment) compilé sans savoir que votre main
était en ligne. La technologie de compilateur moderne est capable d’inclure des limites de module, mais c’est une fonctionnalité avancée, non prise en charge par de nombreux compilateurs plus anciens. Et bien sûr, les avantages de l’inlining ne sont présents que lorsqu’une fonction est appelée très fréquemment; par définition, le main
sera appelé exactement une fois, pas plus, pas moins.
Je vois que la norme le dit, mais la vraie réponse pratique serait aussi simple que de dire que le temps d’exécution ajouté à chaque programme C et C ++ doit appeler un sharepoint l’exécutable. Cette fonction doit avoir un symbole externe (et une adresse lors de l’exécution) pour que l’éditeur de liens puisse le trouver au début de l’exécution. Par conséquent, vous ne pouvez pas le déclarer comme étant en inline
, car le compilateur intégré ne génèrerait pas de symbole externe pour cela.
Depuis sa fonction main (), qui lance l’exécution, lorsque le code est compilé en binary, tout est dans le main()
lui-même. vous pouvez donc dire, c’est déjà en ligne!
Et oui, il est illégal d’utiliser en ligne pour votre programme C ++, c’est plus une syntaxe!
Pour la plupart des combinaisons de compilateur / archétecture, la fonction main()
dans la source devient une fonction raisonnablement normale dans le binary final. C’est seulement parce que c’est pratique sur ces archétectures, non pas parce que la norme le dit.
Sur les archétectures à contraintes de mémoire, de nombreux compilateurs, qui produisent un binary plat (comme le format hexadécimal intex) au lieu d’un conteneur dynamic compatible avec les liens (comme elf ou xcoff), éliminent tout le passe-partout. Certaines architectures ne supportent pas du tout les appels de fonctions (seul un sous-ensemble limité de C ++ est possible sur ces plates-formes).
Afin de prendre en charge la plus grande variété d’architectures et d’environnements de construction, la norme choisit de garder la sémantique de main()
aussi ouverte que possible, de sorte que le compilateur puisse faire ce qui convient à la plus grande variété de plates-formes. Cela signifie que de nombreuses fonctionnalités disponibles dans la langue dans son ensemble ne peuvent pas s’appliquer au démarrage et à l’arrêt de l’application elle-même.
Si vous avez besoin de quelque chose comme un inline main()
(ou réentrance, ou n’importe quelle fonctionnalité), vous pouvez bien sûr appeler la fonction principale quelque chose d’autre:
inline int myMain(int argc, char **argv) { /* whatever */ } int main(int argc, char **argv) { return myMain(argc, argv); }
Les fonctions inline ont une scope statique par défaut. Cela signifie que si nous déclarons main () comme inline, sa scope sera limitée au fichier où il est défini. Cependant, la bibliothèque de démarrage C (fournie par le fournisseur du compilateur) doit être «principale» pour être un symbole global. Certains compilateurs permettent de modifier la fonction du point d’entrée (par exemple, principale) à l’aide de drapeaux de l’éditeur de liens.
Les fonctions inline n’ont généralement pas d’adresse, donc il n’y a pas de moyen portable d’appeler main, main () a besoin d’une adresse sur laquelle le code init peut entrer. Les fonctions en ligne sont censées être bloquées dans la fonction d’appel, si main est en ligne, il doit être intégré au code d’initialisation du programme, qui n’est pas non plus portable.
le système d’exploitation charge les données binarys en mémoire; recherche le point d’entrée (le symbole «principal» dans c / c ++); saute de loin aux adresses de l’étiquette du point d’entrée. Le système d’exploitation ne connaît rien de la fonction principale dans votre code tant que le programme n’est pas chargé.