Les spécificateurs de fonction en C sont un indice au compilateur, le degré d'acceptation est défini par l'implémentation.
Tout d'abord, _Noreturn
spécificateur de fonction (ou, noreturn
en utilisant <stdnoreturn.h>
) est un indice pour le compilateur à propos d'un fichier promesse théorique fait par le programmeur que cette fonction ne reviendra jamais. Sur la base de cette promesse, le compilateur peut prendre certaines décisions, effectuer certaines optimisations pour la génération du code.
IIRC, si une fonction spécifiée avec noreturn
Le spécificateur de fonction retourne finalement à son appelant, soit
- en utilisant et en explicitant
return
déclaration
- en atteignant la fin de la fonction corps
le site le comportement est indéfini . Vous NE DOIT PAS retour de la fonction.
Pour que ce soit clair, utiliser noreturn
spécificateur de fonction ne s'arrête pas une forme de fonction retournant à son appelant. Il s'agit d'une promesse faite par le programmeur au compilateur de lui laisser un peu plus de liberté pour générer un code optimisé.
Maintenant, dans le cas où vous avez fait une promesse plus tôt et plus tard, choisissez de la violer, le résultat est UB. Les compilateurs sont encouragés, mais pas obligés, de produire des avertissements lorsqu'un fichier _Noreturn
semble être capable de retourner à son appelant.
Selon le chapitre §6.7.4, C11
Paragraphe 8
Une fonction déclarée avec un _Noreturn
ne doit pas retourner à son appelant.
et, le paragraphe 12, ( Notez les commentaires ! )
EXAMPLE 2
_Noreturn void f () {
abort(); // ok
}
_Noreturn void g (int i) { // causes undefined behavior if i <= 0
if (i > 0) abort();
}
Pour C++
le comportement est assez similaire. Citation du chapitre §7.6.4, C++14
paragraphe 2 ( c'est moi qui souligne )
Si une fonction f
est appelé là où f
était précédemment déclarée avec le noreturn
et f
éventuellement retourne, le comportement est indéfini. [Note : La fonction peut se terminer par la levée d'une exception. -fin note ]
[Note : Les implémentations sont encouragées à émettre un avertissement si une fonction marquée [[noreturn]]
pourrait retourner. -note de fin ]
3 [ Exemple :
[[ noreturn ]] void f() {
throw "error"; // OK
}
[[ noreturn ]] void q(int i) { // behavior is undefined if called with an argument <= 0
if (i > 0)
throw "positive";
}
-fin de l'exemple ]
22 votes
Pardonnez-moi de vous poser la question, mais en quoi cette fonction de "non-retour" est-elle utile à qui que ce soit ? À quoi sert-elle ou est-elle mal utilisée ?
0 votes
Je veux dire que j'écris souvent des fonctions qui ne reviennent jamais (par exemple, une boucle 'while(true){};' dans un thread d'application), mais pourquoi le compilateur devrait-il le savoir ?
0 votes
N'est-ce pas comme l'option "sans roues" lors de la commande d'une nouvelle voiture ?
16 votes
@MartinJames, votre question ressemble à un double de este :P
17 votes
"J'ai lu cette question sur
noreturn
en C" Non. La question à laquelle vous avez fait référence concerne l'attribut "noreturn
en C++", qui est un langage différent.13 votes
@MartinJames noreturn permet au compilateur d'effectuer certaines optimisations. Le plus important est que la gestion des erreurs qui se terminent par
exit
ou équivalent ne transforme pas les fonctions feuilles en fonctions non-feuilles. Vous pouvez donc avoirassert
ou similaire dans les entrailles les plus profondes de votre code critique pour les performances et cela ne coûte pas plus cher qu'un branchement correctement prédit (au lieu que la fonction doive mettre en place une trame, enregistrer des registres, éviter les registres enregistrés par l'appelant, etc.)1 votes
Exemple trop simpliste ici : godbolt.org/g/n46nc2
foo1
a un "traitement des erreurs" qui peut revenir,foo2
ne le fait pas.foo1
utilise ebx au lieu de eax pour faire le calcul (parce que eax peut être écrasé parerror1
) et puisque ebx est sauvegardé par l'appelant, il doit aussi être jeté sur la pile. (note : ceci doit être compilé à -O1 parce qu'à -O2 gcc trouve une façon plus intelligente de le faire)0 votes
Art OK, merci pour l'info. C'est quand même bizarre ;)
4 votes
@Gerhardh L'OP a peut-être lié une question C++. Mais il s'agit clairement d'un code C et
noreturn
es no (seulement) une chose C++.0 votes
" J'ai lu cette question... "mais il semble que vous n'ayez pas lu ses réponses, n'est-ce pas ?
5 votes
"Ici, la valeur de retour de la fonction." En fait, non, ce n'est pas le cas. Le processeur x86
ret
signifie simplement que l'exécution est transférée à l'appelant. Vous verriez la même chose pour une fonction avec une valeur de retour devoid
. Il retourne contrôle en d'autres termes, pas une valeur. Maintenant, toutes les conventions d'appel x86 renvoient des valeurs dans le formatEAX
mais l'appelant et l'appelé doivent se mettre d'accord sur ce point, donc si la fonction retournevoid
ou rien, alorsEAX
ne contiendra que des déchets. Encore une fois, comme les réponses l'ont dit, selon la norme du langage C, le comportement est indéfini, mais c'est ce que l'asm veut dire.1 votes
Je ne sais pas quoi faire de votre remarque, @user28434. Vous semblez avoir manqué le point de mon commentaire. Dans le code de la question, il y a un commentaire sur la sortie de l'assemblage qui dit "Ici la valeur de retour de la fonction". Cette remarque n'est pas correcte ; une
ret
ne signifie pas que la fonction renvoie une valeur. Tout ce que cela signifie, c'est qu'un transfert de contrôle a lieu. Tout cela n'a rien à voir avec l'instructionnoreturn
annotation. Il s'agit d'une caractéristique du langage C, qui n'est pas pertinente pour le code objet en cours de désassemblage. Si vous essayez de retourner d'unnoreturn
fonction, c'est UB, comme déjà établi.1 votes
C'est juste mentir au compilateur. Le compilateur en sait plus sur le C que vous, donc ne lui mentez pas que cette fonction ne retourne pas.
4 votes
Remarquez, cependant, ce qui est arrivé à
main
il n'y a pas d'instructions après lecall
. Alors quandfunc
retourne, comme vous l'aviez promis, votre compteur de programme sera à un emplacement invalide !0 votes
@MartinJames Un exemple tiré de Swift : Swift possède une instruction de flux de contrôle appelée
guard
déclaration. Cela fonctionne comme une assertion, soit l'assertion est signifiée, soit l'élémentelse
s'exécute. Contrairement à unif
/else
ceelse
Le bloc DOIT exister dans la portée. La sortie de l'étendue peut se faire parbreak
,continue
,return
othrow
. Mais qu'en est-il de l'appelexit()
? Cela sort de la portée, mais comment le compilateur peut-il le savoir ? Swift dispose donc d'un type, appeléNever
qui est le type de retour des fonctions qui ne reviennent jamais. L'appel d'unNever
satisfait à l'exigence de la garde de sortir du champ d'application.2 votes
Une autre utilisation potentielle serait les systèmes embarqués, où l'on ne veut pas que l'appelant empile l'adresse de retour, etc. Pertinent pour certaines parties du code de démarrage, mais aussi pour
void main (void)
qui, dans une application autonome, ne devrait jamais revenir.0 votes
Lorsque j'ai compilé le code affiché, le compilateur a émis un message d'avertissement : "warning : 'noreturn' function does return'" et la flèche associée est dirigée vers l'accolade fermante de la fonction
func()
fonction.0 votes
@MartinJames Sous Linux, les programmes ne retournent pas au noyau pour sortir ; ils doivent effectuer un
exit
afin de mettre fin au processus. Le runtime C utilisenoreturn
pour implémenter ceci : le point d'entrée du programme n'est pas réellementmain
il s'agit d'unnoreturn
qui effectue l'appel système après avoir appelémain
ce qui garantit que le programme se termine correctement sans erreur de segmentation.