Est ‘int main’ un programme C / C ++ valide?

Je demande parce que mon compilateur semble le penser, même si je ne le pense pas.

echo 'int main;' | cc -xc - -Wall
echo 'int main;' | c++ -x c++ - -Wall

Clang n’émet aucun avertissement ou erreur avec ceci, et gcc n’émet que l’avertissement peu fréquent: 'main' is usually a function [-Wmain] , mais seulement lorsqu’il est compilé en C. La spécification d’un -std= ne semble pas avoir d’importance.

Sinon, il comstack et lie bien. Mais à l’exécution, il se termine immédiatement avec SIGBUS (pour moi).

En parcourant les (excellentes) réponses à Que devrait retourner main () en C et C ++? et un rapide grep à travers les spécifications de la langue, il me semble certainement qu’une fonction principale est requirejse. Mais le verbiage de -Wmain de gcc («main» est généralement une fonction) (et le manque d’erreurs ici) semble suggérer le contraire.

Mais pourquoi? Y a-t-il une utilisation étrange ou «historique»? Quelqu’un sait ce que donne?

Mon point, je suppose, est que je pense vraiment que cela devrait être une erreur dans un environnement hébergé, hein?

    Comme la question est balisée en C et C ++, le raisonnement pour C ++ et C serait différent:

    • C ++ utilise la gestion des noms pour faciliter la distinction entre les symboles textuellement identiques de différents types, par exemple une variable globale xyz et une fonction globale indépendante xyz(int) . Cependant, le nom main n’est jamais mutilé.
    • C n’utilise pas le gabarit, il est donc possible qu’un programme confond l’éditeur de liens en fournissant un symbole d’un type à la place d’un symbole différent, et que le programme soit correctement lié.

    C’est ce qui se passe ici: l’éditeur de liens s’attend à trouver le symbole main , et c’est le cas. Il “twig” ce symbole comme s’il s’agissait d’une fonction, car il ne sait pas mieux. La partie de la bibliothèque d’exécution qui passe le contrôle à la main demande à l’éditeur de liens pour main , donc l’éditeur de liens lui atsortingbue le symbole main , ce qui permet à la phase de liaison de se terminer. Bien sûr, cela échoue à l’exécution, car main n’est pas une fonction.

    Voici une autre illustration du même problème:

    fichier xc:

     #include  int foo(); // < <== main() expects this int main(){ printf("%p\n", (void*)&foo); return 0; } 

    fichier yc:

     int foo; // < <== external definition supplies a symbol of a wrong kind 

    compilation:

     gcc xc yc 

    Ceci comstack, et il s'exécuterait probablement, mais c'est un comportement indéfini, car le type du symbole promis au compilateur est différent du symbole réel fourni à l'éditeur de liens.

    En ce qui concerne l'avertissement, je pense que c'est raisonnable: C vous permet de construire des bibliothèques sans fonction main , le compilateur libère le nom main pour d'autres utilisations si vous devez définir une variable main pour une raison inconnue.

    main n’est pas un mot réservé, c’est juste un identifiant prédéfini (comme cin , endl , npos …), vous pouvez donc déclarer une variable appelée main , l’initialiser puis imprimer sa valeur.

    Bien sûr:

    • l’avertissement est utile car il est très sujet aux erreurs;
    • vous pouvez avoir un fichier source sans la fonction main() (bibliothèques).

    MODIFIER

    Quelques références:

    • main n’est pas un mot réservé (C ++ 11):

      La fonction main ne doit pas être utilisée dans un programme. Le lien (3.5) de main est défini par la mise en œuvre. Un programme qui définit main comme supprimé ou qui déclare principal être en inline , static ou constexpr est mal formé. Le nom main 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. – exemple de fin]

      C ++ 11 – [basic.start.main] 3.6.1.3

      [2.11 / 3] […] certains identificateurs sont réservés aux implémentations C ++ et aux bibliothèques standard (17.6.4.3.2) et ne doivent pas être utilisés autrement; aucun diagnostic n’est requirejs.

      [17.6.4.3.2 / 1] Certains jeux de noms et signatures de fonctions sont toujours réservés à l’implémentation:

      • Chaque nom contenant un double trait de soulignement __ ou commençant par un trait de soulignement suivi d’une lettre majuscule (2.12) est réservé à l’implémentation pour toute utilisation.
      • Chaque nom commençant par un trait de soulignement est réservé à l’implémentation pour être utilisé comme nom dans l’espace de noms global.
    • Mots réservés dans les langages de programmation .

      Les mots réservés peuvent ne pas être redéfinis par le programmeur, mais les prédéfinis peuvent souvent être remplacés dans une certaine capacité. C’est le cas de main : il existe des domaines dans lesquels une déclaration utilisant cet identifiant redéfinit sa signification.

    Est int main; un programme C / C ++ valide?

    Il est difficile de savoir ce qu’est un programme C / C ++.

    Est int main; un programme C valide?

    Oui. Une implémentation autonome est autorisée à accepter un tel programme. main ne doit pas avoir de signification particulière dans un environnement autonome.

    Il n’est pas valide dans un environnement hébergé.

    Est int main; un programme C ++ valide?

    Idem.

    Pourquoi ça tombe en panne?

    Le programme ne doit pas avoir de sens dans votre environnement. Dans un environnement autonome, le démarrage et la fin du programme, ainsi que la signification de main , sont définis par l’implémentation.

    Pourquoi le compilateur me prévient-il?

    Le compilateur peut vous avertir de ce qu’il veut, tant qu’il ne rejette pas les programmes conformes. D’autre part, l’avertissement est tout ce qui est nécessaire pour diagnostiquer un programme non conforme. Comme cette unité de traduction ne peut pas faire partie d’un programme hébergé valide, un message de diagnostic est justifié.

    Est-ce que gcc est un environnement autonome ou est-ce un environnement hébergé?

    Oui.

    gcc documente l’ -ffreestanding compilation -ffreestanding . Ajoutez-le et l’avertissement disparaît. Vous voudrez peut-être l’utiliser lors de la construction, par exemple, de kernelx ou de microprogrammes.

    g++ ne documente pas un tel drapeau. Fournir cela semble n’avoir aucun effet sur ce programme. Il est probablement prudent de supposer que l’environnement fourni par g ++ est hébergé. L’absence de diagnostic dans ce cas est un bug.

    C’est un avertissement car il n’est pas techniquement interdit. Le code de démarrage utilisera l’emplacement du symbole “main” et y accédera avec les trois arguments standard (argc, argv et envp). Il ne le fait pas, et au moment du lien ne peut pas vérifier que c’est réellement une fonction, ni même qu’il a ces arguments. C’est aussi la raison pour laquelle int main (int argc, char ** argv) fonctionne – le compilateur ne connaît pas l’argument envp et il se trouve qu’il n’est tout simplement pas utilisé, et qu’il s’agit d’un nettoyage de l’appelant.

    En plaisantant, vous pourriez faire quelque chose comme

     int main = 0xCBCBCBCB; 

    sur une machine x86 et, en ignorant les avertissements et autres éléments similaires, il ne fera pas que comstackr mais fonctionnera aussi.

    Quelqu’un a utilisé une technique similaire pour écrire un exécutable (en quelque sorte) qui s’exécute directement sur plusieurs architectures – http://phrack.org/issues/57/17.html#article . Il a également été utilisé pour gagner le IOCCC – http://www.ioccc.org/1984/mullender/mullender.c .

    Est-ce un programme valide?

    Non.

    Ce n’est pas un programme car il n’a pas de parties exécutables.

    Est-il valide pour comstackr?

    Oui.

    Peut-il être utilisé avec un programme valide?

    Oui.

    Tous les codes compilés ne doivent pas nécessairement être exécutables pour être valides. Les exemples sont les bibliothèques statiques et dynamics.

    Vous avez effectivement créé un fichier object. Ce n’est pas un exécutable valide, cependant un autre programme pourrait être lié à l’object main dans le fichier résultant en le chargeant à l’exécution.

    Est-ce que cela devrait être une erreur?

    Traditionnellement, C ++ permet à l’utilisateur de faire des choses qui peuvent sembler n’avoir aucune utilisation valide, mais qui correspondent à la syntaxe du langage.

    Je veux dire que bien sûr, cela pourrait être reclassé comme une erreur, mais pourquoi? A quoi cela servirait-il que l’avertissement ne soit pas?

    Tant qu’il existe une possibilité théorique d’utiliser cette fonctionnalité dans le code actuel, il est très peu probable qu’un object non fonctionnel appelé main entraîne une erreur en fonction du langage.

    Je voudrais append aux réponses déjà données en citant les normes linguistiques actuelles.

    Est ‘int main’ un programme C valide?

    Réponse courte (mon avis): seulement si votre implémentation utilise un “environnement d’exécution autonome”.

    Toutes les citations suivantes de C11

    5. environnement

    Une implémentation traduit les fichiers source C et exécute les programmes C dans deux environnements de système de traitement de données, appelés environnement de traduction et environnement d’exécution […]

    5.1.2 Environnements d’exécution

    Deux environnements d’exécution sont définis: autonome et hébergé. Dans les deux cas, le démarrage du programme se produit lorsqu’une fonction C désignée est appelée par l’environnement d’exécution.

    5.1.2.1 Environnement autonome

    Dans un environnement autonome (dans lequel l’exécution du programme C peut avoir lieu sans bénéficier d’aucun système d’exploitation), le nom et le type de la fonction appelée au démarrage du programme sont définis par la mise en œuvre.

    5.1.2.2 Environnement hébergé

    Un environnement hébergé n’a pas besoin d’être fourni, mais doit être conforme aux spécifications suivantes, le cas échéant.

    5.1.2.2.1 Démarrage du programme

    La fonction appelée au démarrage du programme est nommée main . […] Il doit être défini avec un type de retour de int et sans parameters […] ou avec deux parameters […] ou équivalents ou d’une autre manière définie par l’implémentation.

    De ceux-ci, on observe ce qui suit:

    • Un programme C11 peut avoir un environnement d’exécution autonome ou hébergé et être valide.
    • S’il y en a un autonome, il n’y a pas besoin d’une fonction principale.
    • Sinon, il doit y en avoir un avec un vale de retour de type int .

    Dans un environnement d’exécution autonome, j’affirmerais qu’il s’agit d’un programme valide qui ne permet pas le démarrage, car il n’ya pas de fonction pour cela comme cela est requirejs au 5.1.2. Dans un environnement d’exécution hébergé, alors que votre code introduit un object nommé main , il ne peut pas fournir de valeur de retour. Je dirais donc que ce n’est pas un programme valide en ce sens, même si le programme ne l’est pas. destiné à être exécuté (on peut vouloir fournir des données seulement par exemple), alors il ne permet tout simplement pas de le faire.

    Est ‘int main’ un programme C ++ valide?

    Réponse courte (mon avis): seulement si votre implémentation utilise un “environnement d’exécution autonome”.

    Citation de C ++ 14

    3.6.1 Fonction principale

    Un programme doit contenir une fonction globale appelée main, qui est le début désigné du programme. Il est défini par l’implémentation si un programme dans un environnement autonome est nécessaire pour définir une fonction principale. […] Il doit avoir un type de retour de type int, mais sinon son type est défini par l’implémentation. […] Le nom principal n’est pas autrement réservé.

    Ici, contrairement à la norme C11, moins de ressortingctions s’appliquent à l’environnement d’exécution autonome, car aucune fonction de démarrage n’est mentionnée, alors que pour un environnement d’exécution hébergé, le cas est sensiblement le même que pour C11.

    Encore une fois, je dirais que pour le cas hébergé, votre code n’est pas un programme C ++ 14 valide, mais je suis sûr que c’est pour le cas autonome.

    Etant donné que ma réponse ne prend en compte que l’environnement d’ exécution , je pense que la réponse de dasblinkenlicht entre en ligne de compte, car la confusion des noms qui se produit dans l’environnement de traduction se produit au préalable. Ici, je ne suis pas sûr que les citations ci-dessus soient observées de manière aussi ssortingcte.

    Mon point, je suppose, est que je pense vraiment que cela devrait être une erreur dans un environnement hébergé, hein?

    L’erreur est à vous. Vous n’avez pas spécifié de fonction nommée main qui renvoie un int et a essayé d’utiliser votre programme dans un environnement hébergé.

    Supposons que vous ayez une unité de compilation qui définisse une variable globale nommée main . Cela pourrait bien être légal dans un environnement autonome car ce qui constitue un programme est laissé à la mise en œuvre dans des environnements autonomes.

    Supposons que vous ayez une autre unité de compilation qui définisse une fonction globale nommée main qui renvoie un int et ne prend aucun argument. C’est exactement ce dont a besoin un programme dans un environnement hébergé.

    Tout va bien si vous n’utilisez que la première unité de compilation dans un environnement autonome et n’utilisez la seconde que dans un environnement hébergé. Et si vous utilisiez les deux dans un seul programme? En C ++, vous avez violé la règle de définition unique. C’est un comportement indéfini. En C, vous avez enfreint la règle qui stipule que toutes les références à un seul symbole doivent être cohérentes. si ce n’est pas le cas, c’est un comportement indéfini. Comportement indéfini est un “sortir de prison, gratuit!” carte aux développeurs d’une implémentation. Tout ce que fait une implémentation en réponse à un comportement indéfini est conforme à la norme. L’implémentation ne doit pas avertir, et encore moins détecter, un comportement indéfini.

    Que se passe-t-il si vous n’utilisez qu’une de ces unités de compilation, mais que vous utilisez la mauvaise (ce que vous avez fait)? En C, la situation est claire. Si vous ne définissez pas la fonction main dans l’un des deux formulaires standard dans un environnement hébergé, le comportement n’est pas défini. Supposons que vous ne définissiez pas le main du tout. Le compilateur / éditeur de liens n’a pas à dire quelque chose à propos de cette erreur. Qu’ils se plaignent est une gentillesse en leur nom. Que le programme C compilé et lié sans erreur soit de votre faute, pas du compilateur.

    C’est un peu moins clair en C ++ car l’échec de la définition de la fonction main dans un environnement hébergé est une erreur plutôt qu’un comportement indéfini (en d’autres termes, il doit être diagnostiqué). Cependant, la règle de définition unique en C ++ signifie que les lieurs peuvent être plutôt stupides. Le job de l’éditeur de liens résout des références externes et, grâce à la règle de définition unique, l’éditeur de liens n’a pas besoin de savoir ce que ces symboles signifient. Vous avez fourni un symbole nommé main , l’éditeur de liens s’attend à voir un symbole nommé main , donc tout est bon pour l’éditeur de liens.

    Pour C jusqu’à présent, il s’agit d’un comportement défini par l’implémentation.

    Comme le dit l’ISO / IEC9899:

    5.1.2.2.1 Démarrage du programme

    1 La fonction appelée au démarrage du programme est nommée main. L’implémentation ne déclare aucun prototype pour cette fonction. Il doit être défini avec un type de retour de int et sans paramètre:

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

    ou avec deux parameters (appelés ici argc et argv, bien que tous les noms puissent être utilisés, car ils sont locaux à la fonction dans laquelle ils sont déclarés):

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

    ou équivalent; ou d’une autre manière définie par la mise en œuvre.

    Non, ce n’est pas un programme valide.

    Pour C ++, cela a été récemment explicitement mal formé par le rapport de défaut 1886: Lien de langage pour main () qui dit:

    Il ne semble pas y avoir de ressortingction à donner à main () un lien explicite avec le langage, mais il devrait probablement être soit mal formé, soit sous condition.

    et une partie de la résolution comprenait le changement suivant:

    Un programme qui déclare une variable principale à scope mondiale ou qui déclare le nom main avec une liaison de langage C (dans n’importe quel espace de noms) est mal formé.

    Nous pouvons trouver cette formulation dans le dernier projet de norme C ++ N4527, qui est le brouillon C ++ 1z.

    Les dernières versions de clang et de gcc en font maintenant une erreur ( voyez-la en direct ):

     error: main cannot be declared as global variable int main; ^ 

    Avant ce rapport de défaut, c’était un comportement indéfini qui ne nécessite pas de diagnostic. D’un autre côté, un code mal formé nécessite un diagnostic, le compilateur peut en faire un avertissement ou une erreur.