web-dev-qa-db-fra.com

Est-ce que main () démarre vraiment un programme C ++?

La section $ 3.6.1/1 de la norme C++ lit,

Un programme doit contenir une fonction globale appelée main, qui est le start désigné du programme.

Considérez maintenant ce code,

int square(int i) { return i*i; }
int user_main()
{ 
    for ( int i = 0 ; i < 10 ; ++i )
           std::cout << square(i) << endl;
    return 0;
}
int main_ret= user_main();
int main() 
{
        return main_ret;
}

Cet exemple de code fait ce que j'ai l'intention de faire, c'est-à-dire imprimer le carré des entiers de 0 à 9, avant entrer dans la fonction main() qui est censée être le "début" du programme.

Je l'ai également compilé avec l'option -pedantic, GCC 4.5.0. Il ne donne aucune erreur, pas même d'avertissement!

Donc ma question est,

Ce code est-il vraiment conforme à la norme?

S'il est conforme à la norme, cela n'invalide-t-il pas ce que dit la norme? main() n'est pas le démarrage de ce programme! user_main() exécutée avant la main().

Je comprends que pour initialiser la variable globale main_ret, La use_main() s'exécute en premier mais c'est tout autre chose; le fait est que cela fait invalide l'instruction citée $ 3.6.1/1 du Standard, car main() n'est PAS le début du programme; c'est en fait le fin de ce programme!


MODIFIER:

Comment définissez-vous le mot "commencer"?

Cela se résume à la définition de la phrase "début du programme" . Alors, comment le définissez-vous exactement?

130
Nawaz

Non, C++ fait beaucoup de choses pour "définir l'environnement" avant l'appel de main; cependant, le principal est le démarrage officiel de la partie "spécifiée par l'utilisateur" du programme C++.

Une partie de la configuration de l'environnement n'est pas contrôlable (comme le code initial pour configurer std :: cout; cependant, une partie de l'environnement est contrôlable comme des blocs globaux statiques (pour initialiser des variables globales statiques). Notez que puisque vous n'avez pas plein contrôle avant principal, vous n'avez pas le contrôle total sur l'ordre dans lequel les blocs statiques sont initialisés.

Après le principal, votre code est conceptuellement "totalement en contrôle" du programme, dans le sens où vous pouvez à la fois spécifier les instructions à exécuter et l'ordre dans lequel les exécuter. Le multi-thread peut réorganiser l'ordre d'exécution du code; mais, vous contrôlez toujours C++ parce que vous avez spécifié que les sections de code s'exécutent (éventuellement) dans le désordre.

82
Edwin Buck

Vous ne lisez pas la phrase correctement.

Un programme doit contenir une fonction globale appelée main, qui est le début désigné du programme.

La norme DÉFINIT le mot "début" aux fins du reste de la norme. Il ne dit pas qu'aucun code ne s'exécute avant l'appel de main. Il indique que le début du programme est considéré comme étant à la fonction main.

Votre programme est conforme. Votre programme n'a pas "démarré" avant le démarrage de main. Le constructeur est appelé avant que votre programme "démarre" selon la définition de "démarrer" dans la norme, mais cela n'a pas d'importance. BEAUCOUP de code est exécuté avant que main soit jamais appelé dans chaque programme, pas seulement cet exemple.

Aux fins de discussion, votre code constructeur est exécuté avant le "démarrage" du programme, et cela est entièrement conforme à la norme.

87
Adam Davis

Votre programme ne sera pas lié et ne fonctionnera donc pas s'il n'y a pas de main. Cependant main () ne provoque pas le démarrage de l'exécution du programme car les objets au niveau du fichier ont des constructeurs qui s'exécutent au préalable et il serait possible d'écrire un programme entier qui exécute sa durée de vie avant que main () soit atteint et laisse main lui-même avoir un corps vide.

En réalité, pour appliquer cela, vous devez avoir un objet qui est construit avant main et son constructeur pour invoquer tout le flux du programme.

Regarde ça:

class Foo
{
public:
   Foo();

 // other stuff
};

Foo foo;

int main()
{
}

Le flux de votre programme proviendrait effectivement de Foo::Foo()

23
CashCow

Vous avez également marqué la question comme "C", puis, en parlant strictement de C, votre initialisation devrait échouer conformément à la section 6.7.8 "Initialisation" de la norme ISO C99.

La plus pertinente dans ce cas semble être la contrainte # 4 qui dit:

Toutes les expressions dans un initialiseur pour un objet qui a une durée de stockage statique doivent être des expressions constantes ou des littéraux de chaîne.

Ainsi, la réponse à votre question est que le code n'est pas conforme à la norme C.

Vous voudrez probablement supprimer la balise "C" si vous n'étiez intéressé que par la norme C++.

15
Remo.D

La section 3.6 dans son ensemble est très claire sur l'interaction de main et des initialisations dynamiques. Le "début désigné du programme" n'est utilisé nulle part ailleurs et est juste descriptif de l'intention générale de main(). Il n'est pas logique d'interpréter cette phrase d'une manière normative qui contredit les exigences plus détaillées et claires de la norme.

10
aschepler

Le compilateur doit souvent ajouter du code avant main () à être conforme à la norme. Parce que la norme spécifie que l'initialisation des globaux/statiques doit être effectuée avant le programme est exécuté. Et comme mentionné, il en va de même pour les constructeurs d'objets placés à portée de fichier (globaux).

Ainsi, la question d'origine est est également pertinente pour C, car dans un programme C, vous auriez toujours les initialisations globales/statiques à faire avant de pouvoir démarrer le programme.

Les normes supposent que ces variables sont initialisées par "magie", car elles ne disent pas comment elles doivent être définies avant l'initialisation du programme. Je pense qu'ils considéraient cela comme quelque chose qui sortait du cadre d'une norme de langage de programmation.

Edit: Voir par exemple ISO 9899: 1999 5.1.2:

Tous les objets avec une durée de stockage statique doivent être initialisés (définis sur leurs valeurs initiales) avant le démarrage du programme. La manière et le moment de cette initialisation ne sont par ailleurs pas spécifiés.

La théorie derrière la façon dont cette "magie" devait être faite remonte à la naissance de C, quand il s'agissait d'un langage de programmation destiné à être utilisé uniquement pour le système d'exploitation UNIX, sur des ordinateurs basés sur la RAM. En théorie, le programme serait en mesure de charger toutes les données pré-initialisées du fichier exécutable dans la RAM, en même temps que le programme lui-même était téléchargé sur la RAM.

Depuis lors, les ordinateurs et les systèmes d'exploitation ont évolué et le C est utilisé dans un domaine beaucoup plus large que prévu initialement. Un PC OS moderne a des adresses virtuelles, etc., et tous les systèmes intégrés exécutent du code à partir de la ROM, pas de la RAM. Il existe donc de nombreuses situations où le RAM ne peut pas être défini "automatiquement").

En outre, la norme est trop abstraite pour connaître quoi que ce soit sur les piles et la mémoire de processus, etc. Ces choses doivent également être effectuées avant le démarrage du programme.

Par conséquent, à peu près tous les programmes C/C++ ont du code init/"copy-down" qui est exécuté avant l'appel de main, afin de se conformer aux règles d'initialisation des normes.

À titre d'exemple, les systèmes embarqués ont généralement une option appelée "démarrage non conforme ISO" où toute la phase d'initialisation est ignorée pour des raisons de performances, puis le code démarre réellement directement à partir de main. Mais ces systèmes ne sont pas conformes aux normes, car vous ne pouvez pas vous fier aux valeurs init des variables globales/statiques.

9
Lundin

main () est une fonction utilisateur appelée par la bibliothèque d'exécution C.

voir aussi: Éviter le principal (point d'entrée) dans un programme C

4
sylvanaar

Votre "programme" renvoie simplement une valeur à partir d'une variable globale. Tout le reste est du code d'initialisation. Ainsi, la norme tient - vous avez juste un programme très trivial et une initialisation plus complexe.

4
Zac Howland

On dirait un jeu de sémantique anglais. L'OP se réfère à son bloc de code d'abord comme "code" et plus tard comme "programme". L'utilisateur écrit le code, puis le compilateur écrit le programme.

2
dSerk

main est appelée après avoir initialisé toutes les variables globales.

Ce que la norme ne spécifie pas, c'est l'ordre d'initialisation de toutes les variables globales de tous les modules et bibliothèques liées statiquement.

1
vz0

Oui, le principal est le "point d'entrée" de chaque programme C++, à l'exception des extensions spécifiques à l'implémentation. Néanmoins, certaines choses se produisent avant main, notamment l'initialisation globale comme pour main_ret.

0
Fred Nurk