96 votes

Comment marche la méthode main() dans C ?

Je sais qu'il y a deux signatures différentes d'écrire la méthode principale -

int main()
{
   //Code
}

ou pour la manipulation argument de ligne de commande, nous l'écrire comme ça

int main(int argc, char * argv[])
{
   //code
}

En C++ je sais que l'on peut surcharger une méthode, mais en C comment le compilateur de traiter ces deux signatures différentes de main méthode?

131voto

Kaz Points 18072

Certaines des fonctionnalités du langage C qui a commencé comme des hacks qui vient de se passer au travail.

Plusieurs signatures pour les principaux, ainsi que la longueur variable des listes d'arguments, est l'une de ces fonctions.

Les programmeurs remarqué que l'on peut passer des arguments supplémentaires à une fonction, et rien de mauvais ne se passe avec leur compilateur.

C'est le cas si les conventions d'appel sont telles que:

  1. La fonction d'appel nettoie les arguments.
  2. Le plus à gauche arguments sont plus près du haut de la pile, ou à la base de la trame de pile, de sorte que fallacieux arguments n'invalident pas l'aborder.

Un ensemble de conventions d'appel qui obéit à ces règles est basée sur la pile passage de paramètres par lequel l'appelant pop les arguments, et ils sont poussés à droite à gauche:

 ;; pseudo-assembly-language
 ;; main(argc, argv, envp); call

 push envp  ;; rightmost argument
 push argv  ;; 
 push argc  ;; leftmost argument ends up on top of stack

 call main

 pop        ;; caller cleans up   
 pop
 pop

Dans les compilateurs où ce type de convention d'appel est le cas, rien de spécial à faire pour prendre en charge les deux types d' main, ou même d'autres types. main peut être une fonction sans arguments, auquel cas il est inconscient des éléments qui ont été poussés sur la pile. Si c'est une fonction de deux arguments, puis il conclut argc et argv comme les deux de plus en haut de la pile des éléments. Si c'est une plate-forme spécifique de trois argument variante avec un environnement pointeur (une extension), qui sera trop de travail: il va trouver que le troisième argument est le troisième élément à partir du haut de la pile.

Et donc fixe appel fonctionne pour tous les cas, permettant à un seul, fixes de début de module à être relié à ce programme. Ce module pourrait être écrit en C, une fonction ressemblant à ceci:

/* I'm adding envp to show that even a popular platform-specific variant
   can be handled. */
extern int main(int argc, char **argv, char **envp);

void __start(void)
{
  /* This is the real startup function for the executable.
     It performs a bunch of library initialization. */

  /* ... */

  /* And then: */
  exit(main(argc_from_somewhere, argv_from_somewhere, envp_from_somewhere));
}

En d'autres termes, cette start module appelle juste un trois-argument principal, toujours. Si le principal ne prend pas d'arguments, ou seulement int, char **, il arrive à bien fonctionner, comme si elle ne prend pas d'arguments, en raison des conventions d'appel.

Si vous décidez de faire ce genre de chose dans votre programme, il serait non-compatibles et considéré comme un comportement non défini par la norme ISO C: déclaration et l'appel d'une fonction dans une manière, et de définir à l'autre. Mais un compilateur de démarrage de truc n'a pas à être portable; il n'est pas guidé par les règles pour la portabilité des programmes.

Mais supposons que les conventions d'appel sont telles qu'il ne peut pas travailler de cette façon. Dans ce cas, le compilateur doit traiter main spécialement. Quand il constate que c'est la compilation de l' main la fonction, il peut générer du code qui est compatible avec, par exemple, un trois argument d'appel.

C'est-à-dire, vous écrivez ceci:

int main(void)
{
   /* ... */
}

Mais lorsque le compilateur voit, il permet d'effectuer une transformation du code, de sorte que la fonction qu'elle compile ressemble plus à ceci:

int main(int __argc_ignore, char **__argv_ignore, char **__envp_ignore)
{
   /* ... */
}

sauf que les noms __argc_ignore n'est pas littéralement d'exister. Aucun de ces noms ont été ajoutés à votre portée, et il n'y aura pas d'avertissement sur inutilisé arguments. La transformation du code, le compilateur émettent code avec le bon lien qui sait ce qu'elle a à nettoyer de trois arguments.

Une autre stratégie de mise en œuvre est pour le compilateur ou peut-être l'éditeur de liens pour générer de l' __start de la fonction (ou peu importe son nom), ou, au moins, sélectionnez-en un à partir de plusieurs pré-compilé des solutions de rechange. L'Information peut être stockée dans le fichier de l'objet sur lequel de la prise en charge des formes d' main est utilisé. L'éditeur de liens pouvez regarder cette info, et de sélectionner la bonne version de la start-up module qui contient un appel à l' main qui est compatible avec le programme de la définition. C implémentations n'ont généralement qu'un petit nombre de prises en charge des formes d' main donc cette approche est faisable.

Les compilateurs pour le C99 langue de toujours avoir à traiter main spécialement, dans une certaine mesure, à l'appui de la bidouille que si la fonction se termine sans un return déclaration, le comportement est comme si return 0 ont été exécutés. Encore une fois, cela peut être traitée par une transformation du code. Le compilateur avis qu'une fonction appelée main est en cours de compilation. Elle vérifie ensuite si la fin du corps est potentiellement accessible. Si oui, il insère return 0;

34voto

al-Acme Points 11571

Il n'y a PAS de surcharge d' main même en C++. La fonction principale est le point d'entrée pour un programme et une seule définition devrait exister.

Pour La Norme C

Pour un environnement hébergé (c'est normal), le standard C99 dit:

5.1.2.2.1 démarrage du Programme

La fonction appelée au démarrage du programme est nommé main. La mise en œuvre déclare aucun prototype de cette fonction. Il doit être défini avec un type de retour d' int et sans paramètres:

int main(void) { /* ... */ }

ou avec deux paramètres (dénommée ci - argc et argv, bien que les noms peuvent être utilisés, car ils sont locales à la fonction dans laquelle elles sont déclarées):

int main(int argc, char *argv[]) { /* ... */ }

ou l'équivalent;9) ou dans certains autres définis par l'implémentation.

9) Ainsi, int peut être remplacé par un typedef nom défini comme int, ou le type d' argv peut être écrite de la char **argv, et ainsi de suite.

Pour la norme C++:

3.6.1 fonction Principale [de base.commencer.principal]

1 Un programme doit contenir une fonction globale appelée principal, qui est le départ du programme. [...]

2 Une mise en œuvre ne doit pas prédéfinir la fonction principale. Cette fonction ne doit pas être surchargé. Il est avoir un type de retour de type int, mais sinon, son type est définie par l'implémentation. Toutes les implémentations doit permettre à la fois de la définition suivante de la page principale:

int main() { /* ... */ }

et

int main(int argc, char* argv[]) { /* ... */ }

La norme C++ explicitement dit: "Elle [la fonction principale] a un type de retour de type int, mais sinon, son type est définie par l'implémentation", et exige la même chose de deux signatures que le C standard.

Dans un environnement hébergé (C environnement qui prend également en charge les bibliothèques C) - les appels de Système d'Exploitation main.

Dans un non-animé de l'environnement (l'Un destiné aux applications embarquées), vous pouvez toujours changer le point d'entrée (ou de sortie) de votre programme à l'aide de la pré-processeur de directives telles que

#pragma startup [priority]
#pragma exit [priority]

Lorsqu'une priorité est une option de nombre entier.

Pragma démarrage exécute la fonction avant le main (priorité-sage) et pragma sortie exécute la fonction après la fonction main. Si il n'y a plus d'un démarrage de la directive priorité décide qui sera exécuté en premier.

8voto

user694733 Points 4040

Il n'est pas nécessaire pour la surcharge. Oui, il y a 2 versions, mais un seul peut être utilisé à la fois.

5voto

6502 Points 42700

C'est l'un de l'étrange les asymétries et les règles spéciales de la C et C++.

À mon avis, il n'existe que pour des raisons historiques, et il n'y a pas sérieuse, la logique derrière elle. Notez que main est spécial aussi pour d'autres raisons (par exemple, main 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 final return déclaration).

Notez également que, même en C++ ce n'est pas une surcharge... soit un programme a la première forme ou il a de la deuxième et de la forme; il ne peut pas avoir les deux.

4voto

Keith Thompson Points 85120

Ce qui est inhabituel main n'est pas qu'il peut être défini à plus d'un titre, c'est qu'il peut seulement être défini de deux façons différentes.

main est une fonction définie par l'utilisateur; la mise en œuvre n'est pas déclarer un prototype.

La même chose est vraie pour foo ou bar, mais vous pouvez définir des fonctions avec celles noms de la manière que vous voulez.

La différence est que, main est invoquée par la mise en œuvre (l'environnement d'exécution), pas seulement par votre propre code. La mise en œuvre n'est pas limitée à l'ordinaire C appel de fonction sémantique, de sorte qu'il peut (et doit) faire face avec quelques variantes -- mais il n'est pas nécessaire pour gérer un nombre infini de possibilités. L' int main(int argv, char *argv[]) formulaire permet arguments de ligne de commande, et int main(void) en C ou int 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, il dépend de la mise en œuvre. La plupart des systèmes ont probablement des conventions d'appel qui sont les deux formes efficacement compatible, et de tous les arguments passés à une main défini sans paramètres sont tranquillement ignoré. Sinon, il ne serait pas difficile pour un compilateur ou l'éditeur de liens pour traiter l' main spécialement. Si vous êtes curieux de savoir comment il fonctionne sur votre système, vous pouvez regarder quelques-assemblée des listes.

Et comme beaucoup de choses en C et C++, les détails sont en grande partie une conséquence de l'histoire et de décisions arbitraires prises par les concepteurs des langues et de leurs prédécesseurs.

Il est à noter que le C et le C++ à la fois permettre à d'autres de mise en œuvre de définitions claires pour main - mais il y a rarement une bonne raison 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 mise en œuvre définis, et n'est même pas nécessairement appelés main.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X