Je demande parce que mon compilateur semble le penser, même si je ne le fais pas.
echo 'int main;' | cc -x c - -Wall
echo 'int main;' | c++ -x c++ - -Wall
Clang n'émet aucun avertissement ou erreur avec cela, et gcc émet uniquement l'avertissement doux: 'main' is usually a function [-Wmain]
, mais uniquement lorsqu'il est compilé en C. Spécifiant un -std=
ne semble pas avoir d'importance.
Sinon, il compile et relie très bien. Mais à l'exécution, il se termine immédiatement par SIGBUS
(pour moi).
En lisant les (excellentes) réponses à Que devrait retourner main () en C et C++? et un rapide aperçu des spécifications du langage, il semblerait certainement pour moi qu'une principale fonction est nécessaire. Mais le verbiage de gcc's -Wmain
('main' est habituellement une fonction) (et le manque d'erreurs ici) semble suggérer autrement.
Mais pourquoi? Y a-t-il une étrange affaire Edge ou une utilisation "historique" pour cela? Quelqu'un sait ce qui donne?
Mon point, je suppose, est que je pense vraiment que cela devrait être une erreur dans un environnement hébergé, hein?
Étant donné que la question est balisée comme C et C++, le raisonnement pour C++ et C serait différent:
xyz
et une fonction globale indépendante xyz(int)
. Cependant, le nom main
n'est jamais modifié.C'est ce qui se passe ici: l'éditeur de liens s'attend à trouver le symbole main
, et il le fait. Il "relie" 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 à main
demande au lieur main
, donc le lieur lui donne le symbole main
, laissant la phase de liaison 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 x.c:
#include <stdio.h>
int foo(); // <<== main() expects this
int main(){
printf("%p\n", (void*)&foo);
return 0;
}
fichier y.c:
int foo; // <<== external definition supplies a symbol of a wrong kind
compilation:
gcc x.c y.c
Cela se compile, et il s'exécutera 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 créer des bibliothèques qui n'ont pas de fonction main
, donc le compilateur libère le nom main
pour d'autres utilisations si vous avez besoin de 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:
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. La liaison (3.5) demain
est définie par l'implémentation. Un programme qui définit principal comme supprimé ou qui déclare principal commeinline
,static
ouconstexpr
est mal formé. Le nommain
n'est pas autrement réservé. [Exemple: les fonctions membres, les classes et les énumérations peuvent être appeléesmain
, tout comme les entités dans d'autres espaces de noms. - exemple de fin]
C++ 11 - [basic.start.main] 3.6.1.3
[2.11/3] [...] certains identifiants sont réservés à l'usage des implémentations C++ et des bibliothèques standard (17.6.4.3.2) et ne doivent pas être utilisé autrement; aucun diagnostic n'est requis.
[17.6.4.3.2/1] Certains ensembles de noms et de signatures de fonctions sont toujours réservés à l'implémentation:
- Chaque nom qui contient un double trait de soulignement __ ou commence par un trait de soulignement suivi d'une lettre majuscule (2.12) est réservé à l'implémentation pour toute utilisation.
- Chaque nom qui commence 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 à certains égards. C'est le cas de main
: il existe des étendues dans lesquelles une déclaration utilisant cet identifiant redéfinit sa signification.
Est int main;
un programme C/C++ valide?
Il n'est pas entièrement clair ce qu'est un programme C/C++.
Est int main;
un programme C valide?
Oui. Une mise en œuvre autonome est autorisée à accepter un tel programme. main
n'a pas besoin d'avoir une signification particulière dans un environnement autonome.
Il est non valide dans un environnement hébergé.
Est int main;
un programme C++ valide?
Idem.
Pourquoi ça plante?
Le programme n'a pas à avoir de sens dans l'environnement votre. Dans un environnement autonome, le démarrage et l'arrêt du programme, ainsi que la signification de main
, sont définis par l'implémentation.
Pourquoi le compilateur m'avertit-il?
Le compilateur peut vous avertir de tout ce qu'il veut, tant qu'il ne rejette pas les programmes conformes. D'un autre côté, l'avertissement est tout ce qui est nécessaire pour diagnostiquer un programme non conforme. Étant donné que cette unité de traduction ne peut pas faire partie d'un programme hébergé valide, un message de diagnostic est justifié.
gcc
est-il un environnement autonome ou est-ce un environnement hébergé?
Oui.
gcc
documente le -ffreestanding
drapeau de compilation. Ajoutez-le et l'avertissement disparaît. Vous voudrez peut-être l'utiliser lors de la construction, par exemple. noyaux ou firmware.
g++
ne documente pas un tel indicateur. Son approvisionnement semble n'avoir aucun effet sur ce programme. Il est probablement sûr de supposer que l'environnement fourni par g ++ est hébergé. L'absence de diagnostic dans ce cas est un bug.
Il s'agit d'un avertissement car il n'est pas techniquement interdit. Le code de démarrage utilisera l'emplacement du symbole "principal" 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 qu'il s'agit bien d'une fonction, ni même qu'il possède ces arguments. C'est aussi pourquoi int main (int argc, char ** argv) fonctionne - le compilateur ne connaît pas l'argument envp et il se trouve qu'il n'est pas utilisé, et c'est le nettoyage de l'appelant.
Pour plaisanter, vous pourriez faire quelque chose comme
int main = 0xCBCBCBCB;
sur une machine x86 et, en ignorant les avertissements et autres choses similaires, il ne se contentera pas de compiler mais fonctionnera également.
Quelqu'un a utilisé une technique similaire à celle-ci 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 l'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 valable de compiler?
Oui.
Peut-il être utilisé avec un programme valide?
Oui.
Il n'est pas nécessaire que tout le code compilé soit exécutable pour être valide. Les exemples sont les bibliothèques statiques et dynamiques.
Vous avez effectivement créé un fichier objet. Ce n'est pas un exécutable valide, mais un autre programme pourrait lier à l'objet main
dans le fichier résultant en le chargeant à l'exécution.
Devrait-il s'agir d'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? À quoi cela servirait-il que l'avertissement ne serve pas?
Tant qu'il existe une possibilité théorique d'utilisation de cette fonctionnalité dans le code réel, il est très peu probable qu'un objet non fonctionnel appelé main
entraîne une erreur selon le langage.
Je voudrais compléter les réponses déjà données en citant les normes linguistiques actuelles.
Réponse courte (mon avis): uniquement 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 programmes C dans deux environnements de système de traitement de données, qui seront 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 aucun avantage d'un système d'exploitation), le nom et le type de la fonction appelée au démarrage du programme sont définis par l'implémentation.
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 s'il est présent.
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 paramètre [...] ou avec deux paramètres [...] ou équivalent ou d'une autre manière définie par l'implémentation.
De ceux-ci, on observe ce qui suit:
Dans un environnement d'exécution autonome, je dirais que c'est un programme valide qui ne permet pas le démarrage, car il n'y a pas de fonction présente pour cela comme requis en 5.1.2. Dans un environnement d'exécution hébergé, alors que votre code introduit un objet nommé main, il ne peut pas fournir de valeur de retour, donc je dirais que ce n'est pas un programme valide dans ce sens, bien qu'on puisse aussi soutiennent comme avant que si le programme n'est pas destiné à être exécuté (on voudra peut-être fournir des données uniquement par exemple), alors il ne permet tout simplement pas de le faire.
Réponse courte (mon avis): uniquement 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 restrictions s'appliquent à l'environnement d'exécution autonome, car aucune fonction de démarrage n'est mentionnée du tout, tandis que pour un environnement d'exécution hébergé, le cas est à peu près 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 indépendant.
Étant donné que ma réponse ne prend en compte que l'environnement exécution, je pense que la réponse de dasblinkenlicht entre en jeu, car le changement de nom se produisant dans l'environnement translation se produit à l'avance. Ici, je ne suis pas sûr que les citations ci-dessus soient respectées de manière aussi stricte.
Mon point, je suppose, est que je pense vraiment que cela devrait être une erreur dans un environnement hébergé, hein?
L'erreur est la vôtre. Vous n'avez pas spécifié de fonction nommée main
qui renvoie un int
et avez essayé d'utiliser votre programme dans un environnement hébergé.
Supposons que vous ayez une unité de compilation qui définit 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éfinit une fonction globale nommée main
qui renvoie un int
et ne prend aucun argument. C'est exactement ce dont un programme dans un environnement hébergé a besoin.
Tout va bien si vous utilisez uniquement la première unité de compilation dans un environnement autonome et utilisez uniquement la seconde dans un environnement hébergé. Et si vous utilisez les deux dans un seul programme? En C++, vous avez violé la règle d'une définition. C'est un comportement indéfini. En C, vous avez violé la règle qui stipule que toutes les références à un seul symbole doivent être cohérentes; s'ils ne le sont pas, c'est un comportement indéfini. Un comportement indéfini est un "sortir de prison, gratuitement!" carte aux développeurs d'une implémentation. Tout ce qu'une implémentation fait en réponse à un comportement non défini est conforme à la norme. L'implémentation n'a pas à avertir, et encore moins à détecter, un comportement indéfini.
Et si vous n'utilisez qu'une seule de ces unités de compilation, mais que vous en utilisez une mauvaise (ce que vous avez fait)? En C, la situation est claire. Le fait de ne pas définir la fonction main
dans l'un des deux formulaires standard dans un environnement hébergé est un comportement non défini. Supposons que vous n'ayez pas du tout défini main
. Le compilateur/éditeur de liens n'a rien à dire sur cette erreur. Qu'ils se plaignent est une gentillesse en leur nom. Que le programme C compilé et lié sans erreur soit votre faute, pas celle 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 éditeurs de liens peuvent être plutôt stupides. Le travail de l'éditeur de liens consiste à résoudre les 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 va bien en ce qui concerne 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ètres:
int main(void) { /* ... */ }
ou avec deux paramètres (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 l'implémentation.
Non, ce n'est pas un programme valide.
Pour C++, cela a récemment été explicitement rendu mal formé par rapport de défaut 1886: liaison de langage pour main () qui dit:
Il ne semble pas y avoir de restriction à donner à main () un lien de langage explicite, mais il devrait probablement être mal formé ou soutenu conditionnellement.
et une partie de la résolution comprenait le changement suivant:
Un programme qui déclare une variable main à portée globale ou qui déclare le nom main avec une liaison en langage C (dans n'importe quel espace de noms) est mal formé.
Nous pouvons trouver ce libellé dans la dernière C++ draft standard N4527 qui est le C++ 1z draft.
Les dernières versions de clang et de gcc en font maintenant une erreur (voir 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écessitait 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.