Je sais qu'il y a deux signatures différentes pour écrire la méthode principale -
int main()
{
//Code
}
ou pour gérer l'argument de la ligne de commande, nous l'écrivons comme-
int main(int argc, char * argv[])
{
//code
}
Dans C++
Je sais, nous pouvons surcharger une méthode, mais C
comment le compilateur gère-t-il ces deux signatures de main
fonction différentes?
main
méthode dans un seul programme dansC
(ou, vraiment, dans à peu près n'importe quel langage avec une telle construction).main
- je recommande le livre classique de John R. Levines "Linkers & Loaders".int main(void)
, nonint main()
(bien que je n'ai jamais vu un compilateur qui rejette leint main()
formulaire).()
formulaire est obsolescent, et il n'est pas clair qu'il soit même autorisémain
(à moins que l'implémentation ne le documente spécifiquement comme un formulaire autorisé). Le standard C (voir 5.1.2.2.1 Démarrage du programme) ne mentionne pas le()
formulaire, qui n'est pas tout à fait équivalent au()
formulaire. Les détails sont trop longs pour ce commentaire.Réponses:
Certaines des fonctionnalités du langage C ont commencé comme des hacks qui ont fonctionné.
Les signatures multiples pour les listes d'arguments principales et de longueur variable font partie de ces fonctionnalités.
Les programmeurs ont remarqué qu'ils peuvent passer des arguments supplémentaires à une fonction, et rien de mauvais ne se produit avec leur compilateur donné.
C'est le cas si les conventions d'appel sont telles que:
Un ensemble de conventions d'appel qui obéit à ces règles est le passage de paramètre basé sur la pile, par lequel l'appelant affiche les arguments, et ils sont poussés de droite à gauche:
Dans les compilateurs où ce type de convention d'appel est le cas, rien de spécial ne doit être fait pour prendre en charge les deux types de
main
, voire des types supplémentaires.main
peut être une fonction d'aucun argument, auquel cas il est inconscient des éléments qui ont été poussés sur la pile. Si c'est une fonction de deux arguments, alors il trouveargc
etargv
comme les deux éléments de pile supérieurs. S'il s'agit d'une variante à trois arguments spécifique à la plate-forme avec un pointeur d'environnement (une extension commune), cela fonctionnera également: il trouvera ce troisième argument comme troisième élément à partir du haut de la pile.Ainsi, un appel fixe fonctionne dans tous les cas, permettant à un seul module de démarrage fixe d'être lié au programme. Ce module pourrait être écrit en C, comme une fonction ressemblant à ceci:
En d'autres termes, ce module de démarrage appelle simplement un main à trois arguments, toujours. Si main ne prend aucun argument, ou seulement
int, char **
, cela fonctionne correctement, ainsi que s'il ne prend aucun argument, en raison des conventions d'appel.Si vous deviez faire ce genre de chose dans votre programme, ce serait non portable et considéré comme un comportement indéfini par ISO C: déclarer et appeler une fonction d'une manière et la définir d'une autre. Mais l'astuce de démarrage d'un compilateur n'a pas besoin d'être portable; il n'est pas guidé par les règles des programmes portables.
Mais supposons que les conventions d'appel soient telles que cela ne peut pas fonctionner de cette façon. Dans ce cas, le compilateur doit traiter
main
spécialement. Lorsqu'il remarque qu'il compile lamain
fonction, il peut générer du code compatible avec, par exemple, un appel à trois arguments.C'est-à-dire que vous écrivez ceci:
Mais lorsque le compilateur le voit, il effectue essentiellement une transformation de code afin que la fonction qu'il compile ressemble plus à ceci:
sauf que les noms
__argc_ignore
n'existent pas littéralement. Aucun de ces noms n'est introduit dans votre portée, et il n'y aura aucun avertissement concernant les arguments inutilisés. La transformation de code oblige le compilateur à émettre du code avec la liaison correcte qui sait qu'il doit nettoyer trois arguments.Une autre stratégie d'implémentation consiste pour le compilateur ou peut-être l'éditeur de liens à générer la
__start
fonction (ou quel que soit son nom), ou au moins en sélectionner une parmi plusieurs alternatives précompilées. Des informations peuvent être stockées dans le fichier objet sur lequel des formulaires pris en charge demain
est utilisé. L'éditeur de liens peut consulter ces informations et sélectionner la version correcte du module de démarrage qui contient un appelmain
compatible avec la définition du programme. Les implémentations C n'ont généralement qu'un petit nombre de formes prises en charge,main
donc cette approche est faisable.Les compilateurs pour le langage C99 doivent toujours traiter
main
spécialement, dans une certaine mesure, pour prendre en charge le hack que si la fonction se termine sansreturn
instruction, le comportement est comme si ellereturn 0
était exécutée. Ceci, encore une fois, peut être traité par une transformation de code. Le compilateur remarque qu'une fonction appeléemain
est en cours de compilation. Ensuite, il vérifie si l'extrémité du corps est potentiellement accessible. Si tel est le cas, il insère unreturn 0;
la source
Il n'y a PAS de surcharge de
main
même en C ++. La fonction principale est le point d'entrée d'un programme et une seule définition doit exister.Pour la norme C
Pour le C ++ standard:
Le standard C ++ dit explicitement "Elle [la fonction principale] doit avoir un type de retour de type int, mais sinon son type est défini par l'implémentation", et nécessite les deux mêmes signatures que le standard C.
Dans un environnement hébergé ( environnement AC qui prend également en charge les bibliothèques C) - les appels du système d'exploitation
main
.Dans un environnement non hébergé (destiné aux applications embarquées), vous pouvez toujours modifier le point d'entrée (ou de sortie) de votre programme en utilisant les directives du pré-processeur comme
Où la priorité est un nombre entier facultatif.
Le démarrage de Pragma exécute la fonction avant que le main (par priorité) et la sortie de pragma exécute la fonction après la fonction principale. S'il y a plus d'une directive de démarrage, la priorité décide de celle qui s'exécutera en premier.
la source
Il n'y a pas besoin de surcharge. Oui, il existe 2 versions, mais une seule peut être utilisée à la fois.
la source
C'est l'une des étranges asymétries et règles spéciales du langage C et C ++.
À mon avis, cela n'existe que pour des raisons historiques et il n'y a pas de réelle logique sérieuse derrière cela. Notez que
main
c'est spécial aussi pour d'autres raisons (par exemplemain
en C ++ ne peut pas être récursif et vous ne pouvez pas prendre son adresse et en C99 / C ++, vous êtes autorisé à omettre un finalreturn
instruction ).Notez également que même en C ++, ce n'est pas une surcharge ... soit un programme a la première forme, soit la deuxième forme; il ne peut pas avoir les deux.
la source
return
instruction en C (depuis C99).main()
et prendre son adresse; C ++ applique des limites que C n'applique pas.argc
lors de la récurrence (5.1.2.2.1 ne spécifie pas de limitations surargc
etargv
s'appliquent uniquement à l'appel initial àmain
).Ce qui est inhabituel au sujet
main
n'est pas qu'elle peut être définie dans plus d'une façon, il est qu'il peut seulement être défini dans l' une de deux façons différentes.main
est une fonction définie par l'utilisateur; l'implémentation ne déclare pas de prototype pour cela.La même chose est vraie pour
foo
oubar
, mais vous pouvez définir des fonctions avec ces noms comme vous le souhaitez.La différence est qu'elle
main
est appelée par l'implémentation (l'environnement d'exécution), pas seulement par votre propre code. L'implémentation n'est pas limitée à la sémantique ordinaire des appels de fonction C, elle peut donc (et doit) traiter quelques variations - mais elle n'est pas obligée de gérer une infinité de possibilités. Leint main(int argc, char *argv[])
formulaire permet les arguments de ligne de commande, etint main(void)
en C ouint main()
en C ++ est juste une commodité pour les programmes simples qui n'ont pas besoin de traiter les arguments de ligne de commande.Quant à la façon dont le compilateur gère cela, cela dépend de l'implémentation. La plupart des systèmes ont probablement des conventions d'appel qui rendent les deux formes effectivement compatibles, et tous les arguments passés à un
main
défini sans paramètre sont discrètement ignorés. Sinon, il ne serait pas difficile pour un compilateur ou un éditeur de liens de traitermain
spécialement. Si vous êtes curieux de savoir comment cela fonctionne sur votre système , vous pouvez consulter certaines listes d'assemblages.Et comme beaucoup de choses en C et C ++, les détails sont en grande partie le résultat de l'histoire et des décisions arbitraires prises par les concepteurs des langages et leurs prédécesseurs.
Notez que C et C ++ autorisent tous les deux d'autres définitions définies par l'implémentation pour
main
- mais il y a rarement de bonnes raisons de les utiliser. Et pour les implémentations autonomes (comme les systèmes embarqués sans OS), le point d'entrée du programme est défini par l'implémentation et n'est même pas nécessairement appelémain
.la source
Le
main
est juste un nom pour une adresse de départ décidée par l'éditeur de liens oùmain
est le nom par défaut. Tous les noms de fonction d'un programme sont des adresses de départ où la fonction démarre.Les arguments de fonction sont poussés / sautés sur / à partir de la pile, donc s'il n'y a pas d'arguments spécifiés pour la fonction, il n'y a pas d'arguments poussés / sautés sur / hors de la pile. C'est ainsi que main peut fonctionner à la fois avec ou sans arguments.
la source
où la variable argc stocke le nombre de données passées et argv est un tableau de pointeurs vers char qui pointe vers les valeurs transmises depuis la console. Sinon c'est toujours bon d'accompagner
Cependant, dans tous les cas, il peut y avoir un et un seul main () dans un programme, car c'est le seul point où à partir d'un programme commence son exécution et donc il ne peut pas y en avoir plus d'un. (j'espère que c'est digne)
la source
Une question similaire a été posée auparavant: Pourquoi une fonction sans paramètres (par rapport à la définition de fonction réelle) se compile-t-elle?
L'une des réponses les mieux classées était:
Donc, je suppose que c'est comment
main
est déclaré (si vous pouvez appliquer le terme "déclaré" àmain
). En fait, vous pouvez écrire quelque chose comme ceci:et il sera toujours compilé et exécuté.
la source
main
, car il y a un problème non encore mentionné: encore plus d' arguments pourmain
! «Unix (mais pas Posix.1) et Microsoft Windows» ajoutentchar **envp
(je me souviens que DOS l'a également autorisé, n'est-ce pas?), Et Mac OS X et Darwin ajoutent encore un autre pointeur char * «d'informations arbitraires fournies par le système d'exploitation». wikipediaVous n'avez pas besoin de remplacer cela, car une seule sera utilisée à la fois Oui, il existe 2 versions différentes de la fonction principale
la source