Avant d'entrer dans le vif du sujet, il est important de souligner que le programme est mal formé, conformément à l'article 2 de la loi sur la protection des données. rapport de défaut 1886 : Lien avec le langage pour main() :
[...] Un programme qui déclare une variable main à portée globale ou qui déclare le nom main avec un lien au langage C (dans n'importe quel espace de noms) est mal formé. [...]
Dans les versions les plus récentes de clang et de gcc, cela constitue une erreur et le programme ne sera pas compilé ( voir l'exemple de gcc en direct ):
error: cannot declare '::main' to be a global variable
int main = ( std::cout << "C++ is excellent!\n", 195 );
^
Alors pourquoi n'y avait-il pas de diagnostic dans les anciennes versions de gcc et clang ? Ce rapport de défaut n'a même pas eu de résolution proposée avant fin 2014 et donc ce cas n'a été que très récemment explicitement mal formé, ce qui nécessite un diagnostic.
Avant cela, il semble que ce soit un comportement indéfini puisque nous violons une règle de base de l'UE. est l'exigence du projet de norme C++ de la section 3.6.1
[basic.start.main] :
Un programme doit contenir une fonction globale appelée main, qui constitue le début désigné du programme. [...]
Le comportement non défini est imprévisible et ne nécessite pas de diagnostic. L'incohérence que nous constatons en reproduisant le comportement est un comportement indéfini typique.
Alors, que fait réellement ce code et pourquoi, dans certains cas, il produit des résultats ? Voyons ce que nous avons :
declarator
| initializer----------------------------------
| | |
v v v
int main = ( std::cout << "C++ is excellent!\n", 195 );
^ ^ ^
| | |
| | comma operator
| primary expression
global variable of type int
Nous avons main
qui est un int déclarée dans l'espace de noms global et en cours d'initialisation, la variable a une durée de stockage statique. Il est défini dans l'implémentation si l'initialisation aura lieu avant une tentative d'appel à la fonction main
est faite mais il semble que gcc le fasse avant d'appeler main
.
Le code utilise le opérateur de virgule l'opérande de gauche est une expression de valeur rejetée et est utilisé ici uniquement pour l'effet secondaire de l'appel à std::cout
. Le résultat de l'opérateur virgule est l'opérande de droite qui, dans ce cas, est la valeur pr. 195
qui est assigné à la variable main
.
Nous pouvons voir sergej fait remarquer l'assemblage généré montre que cout
est appelé pendant l'initialisation statique. Bien que le point le plus intéressant pour la discussion voir la session live de godbolt serait la suivante :
main:
.zero 4
et le suivant :
movl $195, main(%rip)
Le scénario probable est que le programme saute au symbole main
s'attendre à ce que le code valide soit là et dans dans certains cas, il y aura des erreurs de segmentation . Donc, si c'est le cas, nous nous attendons à stocker un code machine valide dans la variable main
pourrait conduire à programme viable en supposant que nous nous trouvons dans un segment qui permet l'exécution du code. Nous pouvons voir cette entrée de 1984 de l'IOCCC hace juste que .
Il semble que l'on puisse faire en sorte que gcc le fasse en C en utilisant ( voir en direct ):
const int main = 195 ;
Il se met en défaut si la variable main
n'est pas const, probablement parce qu'il n'est pas situé dans un emplacement exécutable. commentez ici ce qui m'a donné cette idée.
Voir aussi Réponse FUZxxl ici à une version spécifique C de cette question.
1 votes
MSVC++14.0 se plaint de - LNK1561 : le point d'entrée doit être défini
0 votes
Utilisez-vous le
strict
paramètre du compilateur ? Quels sont les paramètres du compilateur que vous utilisez ?0 votes
@Fireho : MSVS 2010 montre également une erreur de liaison. Mais pourquoi g++ l'accepte-t-il ?
11 votes
@ : pourquoi la balise language lawyer est-elle nécessaire ?
1 votes
Il compile OK mais le noyau dumps g++ 4.8.4
1 votes
Cela fonctionne pour moi avec g++ 4.8.3 sur RHEL 7.
14 votes
Notez que
195
est l'opcode de la fonctionRET
et que dans la convention d'appel du C, l'appelant efface la pile.2 votes
@PravasiMeet "alors comment ce programme s'exécute" - ne pensez-vous pas que le code d'initialisation d'une variable devrait être exécuté (même sans l'élément
main()
en fait, ils n'ont aucun rapport entre eux).1 votes
Généralement, il y a un morceau de code dans le runtime qui exécute les constructeurs statiques avant d'appeler
main
(bien que la norme permette techniquement que l'initialisation statique n'ait lieu qu'après l'entrée en vigueur de la normemain
commence).4 votes
Je fais partie de ceux qui ont constaté que le programme se bloque tel quel (linux 64 bits, g++ 5.1/clang 3.6). Je peux cependant rectifier cela en le modifiant en
int main = ( std::cout << "C++ is excellent!\n", exit(0),1 );
(et comprenant<cstdlib>
), bien que le programme reste juridiquement mal formé.11 votes
@Brian Vous devriez mentionner l'architecture lorsque vous faites des déclarations de ce genre. Le monde entier n'est pas un VAX. Ou x86. Ou quoi que ce soit.
0 votes
Brian, il n'y a pas de "convention d'appel C", car le C ne se soucie pas des implémentations inférieures. La convention d'appel dépend de l'architecture et de l'ABI. Même en x86, il existe plusieurs conventions pour le nettoyage de l'appelant ou du destinataire. Et 195 peut être RET en x86 mais pas sur d'autres architectures.
0 votes
@LuuVinhPhúc : Il est un peu irréaliste de penser qu'une ABI peut être uniforme dans tous les langages. Certains langages ont besoin de varargs, d'autres de classes. La "convention d'appel C" est une réalité pratique, et signifie que l'ABI supporte les varargs (qui impliquent un nettoyage de l'appelant).
0 votes
Il pourrait être utile de préciser, en modifiant votre question, que, bien que la validité de ce programme soit importante, vous vous concentrez davantage sur les mécanismes de ce qui se passe. Cela aidera à la distinguer de la question plus restreinte de Est-ce que c'est valable .
0 votes
J'ai changé le titre parce qu'il décrit mieux la question réelle, même si je pense qu'il pourrait être encore amélioré. Peut-être que quelqu'un d'autre pourra trouver une meilleure formulation, j'ai toujours du mal à trouver un bon titre.
0 votes
@ShafikYaghmour : Non, il ne faut pas changer le titre. Le titre que j'ai donné était bon.
1 votes
Dire "code délicat" ne décrit pas ce que la question demande et de tels titres génériques sont découragés. En général, pour les questions chaudes sur les réseaux, quelqu'un a déjà trouvé un meilleur titre, alors j'attends que quelqu'un d'autre le fasse.
0 votes
@ShafikYaghmour : Mais ne pensez-vous pas que ma question montre des efforts de recherche ?
1 votes
Je pense que c'était une bonne question, même si je ne le pensais pas : Je n'y aurais pas répondu et j'aurais peut-être rétrogradé et/ou voté la fermeture.
2 votes
Voir ceci meta post : Le titre de la question (ainsi que le corps de la question) doit décrire le problème réel, indépendamment de la façon dont l'auteur de la question initiale l'a perçu, encadré ou décrit.
0 votes
Question connexe : stackoverflow.com/q/2252380/252489