Depuis que j'ai réalisé il y a de nombreuses années, que cela ne produisait pas d'erreur par défaut (au moins dans GCC), je me suis toujours demandé pourquoi?
Je comprends que vous pouvez émettre des indicateurs de compilateur pour produire un avertissement, mais cela ne devrait-il pas toujours être une erreur? Pourquoi est-il logique qu'une fonction non vide ne renvoyant pas de valeur soit valide?
Un exemple comme demandé dans les commentaires:
#include <stdio.h>
int stringSize()
{
}
int main()
{
char cstring[5];
printf( "the last char is: %c\n", cstring[stringSize()-1] );
return 0;
}
... compile.
-Werror=return-type
traitera juste cet avertissement comme une erreur. J'ai simplement ignoré l'avertissement et les quelques minutes de frustration à la recherche d'unthis
pointeur invalide m'ont conduit ici et à cette conclusion.std::optional
fonction sans retourner renvoie un "vrai" facultatifRéponses:
Les normes C99 et C ++ n'exigent pas que les fonctions retournent une valeur. L'instruction de retour manquante dans une fonction de retour de valeur sera définie (à renvoyer
0
) uniquement dans lamain
fonction.La justification inclut que vérifier si chaque chemin de code renvoie une valeur est assez difficile, et une valeur de retour pourrait être définie avec l'assembleur intégré ou d'autres méthodes délicates.
À partir du brouillon C ++ 11 :
§ 6.6.3 / 2
§ 3.6.1 / 5
Notez que le comportement décrit dans C ++ 6.6.3 / 2 n'est pas le même en C.
gcc vous donnera un avertissement si vous l'appelez avec l'option -Wreturn-type.
Juste par curiosité, regardez ce que fait ce code:
Ce code a un comportement formellement non défini et, en pratique, il appelle la convention et l' architecture dépendante. Sur un système particulier, avec un compilateur particulier, la valeur de retour est le résultat de la dernière évaluation d'expression, stockée dans le
eax
registre du processeur de ce système.la source
throw
oulongjmp
, le compilateur devrait-il exiger un injoignablereturn
suite à l'appel de la fonction non retournée? Le cas où cela n'est pas nécessaire n'est pas très courant, et l'exigence de l'inclure même dans de tels cas n'aurait probablement pas été onéreuse, mais la décision de ne pas l'exiger est raisonnable.gcc ne vérifie pas par défaut que tous les chemins de code renvoient une valeur car en général cela ne peut pas être fait. Cela suppose que vous savez ce que vous faites. Prenons un exemple courant utilisant des énumérations:
Vous le programmeur savez que, sauf bogue, cette méthode renvoie toujours une couleur. gcc croit que vous savez ce que vous faites afin de ne pas vous obliger à mettre un retour au bas de la fonction.
javac, d'autre part, essaie de vérifier que tous les chemins de code renvoient une valeur et renvoie une erreur s'il ne peut pas prouver qu'ils le font tous. Cette erreur est imposée par la spécification du langage Java. Notez que parfois c'est faux et que vous devez mettre une déclaration de retour inutile.
C'est une différence philosophique. C et C ++ sont des langages plus permissifs et plus fiables que Java ou C # et certaines erreurs dans les nouveaux langages sont des avertissements en C / C ++ et certains avertissements sont ignorés ou désactivés par défaut.
la source
System.exit()
ne revient jamais.System.exit()
ne revient jamais. Je l'ai recherché ( java.sun.com/j2se/1.4.2/docs/api/java/lang/… ), et la documentation dit simplement qu'il "ne retourne jamais normalement". Je me demande ce que cela signifie ...Vous voulez dire, pourquoi sortir de la fin d'une fonction de retour de valeur (c'est-à-dire sortir sans explicite
return
) n'est pas une erreur?Premièrement, en C, le fait qu'une fonction retourne quelque chose de significatif ou non n'est critique que lorsque le code en cours d'exécution utilise réellement la valeur retournée. Peut-être que la langue ne voulait pas vous forcer à renvoyer quoi que ce soit quand vous savez que vous n'allez pas l'utiliser de toute façon la plupart du temps.
Deuxièmement, apparemment, la spécification du langage ne voulait pas forcer les auteurs du compilateur à détecter et à vérifier tous les chemins de contrôle possibles pour la présence d'un explicite
return
(bien que dans de nombreux cas ce ne soit pas si difficile à faire). De plus, certains chemins de contrôle peuvent conduire à des fonctions non retournées - le trait généralement inconnu du compilateur. De tels chemins peuvent devenir une source de faux positifs ennuyeux.Notez également que C et C ++ diffèrent dans leurs définitions du comportement dans ce cas. En C ++, le simple fait d'écouler la fin d'une fonction retournant une valeur est toujours un comportement indéfini (indépendamment du fait que le résultat de la fonction soit utilisé par le code appelant). En C, cela provoque un comportement indéfini uniquement si le code appelant essaie d'utiliser la valeur renvoyée.
la source
return
instructions de la fin demain()
?main
c'est spécial à cet égard.Il est légal sous C / C ++ de ne pas revenir d'une fonction qui prétend retourner quelque chose. Il existe un certain nombre de cas d'utilisation, tels que l'appel
exit(-1)
ou une fonction qui l'appelle ou lève une exception.Le compilateur ne rejettera pas le C ++ légal même s'il conduit à UB si vous lui demandez de ne pas le faire. En particulier, vous demandez qu'aucun avertissement ne soit généré. (Gcc en active toujours certains par défaut, mais une fois ajoutés, ceux-ci semblent correspondre aux nouvelles fonctionnalités et non aux nouveaux avertissements pour les anciennes fonctionnalités)
Changer le gcc no-arg par défaut pour émettre des avertissements pourrait être un changement radical pour les scripts existants ou créer des systèmes. Ceux qui sont bien conçus
-Wall
et traitent les avertissements ou basculent les avertissements individuels.Apprendre à utiliser une chaîne d'outils C ++ est un obstacle à l'apprentissage du programmeur C ++, mais les chaînes d'outils C ++ sont généralement écrites par et pour des experts.
la source
Makefile
je l'ai en cours d'exécution avec-Wall -Wpedantic -Werror
, mais c'était un script de test unique que j'ai oublié de fournir les arguments.-Wduplicated-cond
partie d'un-Wall
bootstrap cassé de GCC. Certains avertissements qui semblent appropriés dans la plupart des codes ne sont pas appropriés dans tous les codes. C'est pourquoi ils ne sont pas activés par défaut.int foo() { exit(-1); }
ne retourne pasint
d'une fonction qui "prétend retourner int". Ceci est légal en C ++. Maintenant, cela ne renvoie rien ; la fin de cette fonction n'est jamais atteinte. En fait, atteindre la finfoo
serait un comportement indéfini. Ignorer les cas de fin de processus,int foo() { throw 3.14; }
prétend également revenirint
mais ne le fait jamais.void* foo(void* arg) { pthread_exit(NULL); }
c'est bien pour la même raison (quand sa seule utilisation est viapthread_create(...,...,foo,...);
)Je pense que c'est à cause du code hérité (C n'a jamais requis de déclaration de retour, tout comme C ++). Il existe probablement une énorme base de code reposant sur cette «fonctionnalité». Mais au moins il y a un
-Werror=return-type
drapeau sur de nombreux compilateurs (y compris gcc et clang).la source
Dans certains cas limités et rares, la sortie de la fin d'une fonction non vide sans renvoyer une valeur peut être utile. Comme le code spécifique MSVC suivant:
Cette fonction renvoie pi en utilisant l'assembly x86. Contrairement à l'assemblage dans GCC, je ne connais aucun moyen d'utiliser
return
pour faire cela sans impliquer des frais généraux dans le résultat.Autant que je sache, les compilateurs C ++ traditionnels devraient émettre au moins des avertissements pour un code apparemment invalide. Si je fais le corps de
pi()
vide, GCC / Clang rapportera un avertissement, et MSVC rapportera une erreur.Les gens ont mentionné des exceptions et
exit
dans certaines réponses. Ce ne sont pas des raisons valables. Le fait de lever une exception ou d'appeler neexit
fera pas que l'exécution de la fonction se termine. Et les compilateurs le savent: écrire une instruction throw ou appelerexit
le corps vide depi()
arrêtera tout avertissement ou erreur d'un compilateur.la source
void
fonction après que inline asm laisse une valeur dans le registre de valeur de retour. (x87st0
dans ce cas, EAX pour entier. Et peut-être xmm0 dans une convention d'appel qui renvoie float / double en xmm0 au lieu de st0). La définition de ce comportement est spécifique à MSVC; même pas clang avec-fasm-blocks
pour supporter la même syntaxe rend cela sûr. Voir Does __asm {}; retourner la valeur de eax?Dans quelles circonstances cela ne produit-il pas une erreur? S'il déclare un type de retour et ne renvoie rien, cela me semble être une erreur.
La seule exception à laquelle je peux penser est la
main()
fonction, qui n'a pas du tout besoin d'unereturn
déclaration (au moins en C ++; je n'ai aucune des normes C à portée de main). S'il n'y a pas de retour, il agira comme si c'étaitreturn 0;
la dernière instruction.la source
main()
a besoin d'unreturn
en C.return <value>;
déclarationreturn 0;
à la fin demain()
(etmain()
seulement). Mais c'est un bon style à ajouterreturn 0;
quand même.On dirait que vous devez activer les avertissements de votre compilateur:
la source
return;
dans une fonction non-void, et c'est une violation de contrainte à utiliserreturn <value>;
dans une fonction void. Mais ce n'est pas le sujet, je crois. L'OP, tel que je l'ai compris, consiste à quitter une fonction non vide sans areturn
(simplement permettre au contrôle de s'écouler à la fin de la fonction). Ce n'est pas une violation de contrainte, AFAIK. La norme dit simplement que c'est toujours UB en C ++ et parfois UB en C.Il s'agit d'une violation de contrainte dans c99, mais pas dans c89. Contraste:
c89:
c99:
Même en
--std=c99
mode, gcc ne lancera qu'un avertissement (bien que sans avoir besoin d'activer des-W
indicateurs supplémentaires , comme cela est requis par défaut ou dans c89 / 90).Modifier pour ajouter qu'en c89, "atteindre le
}
qui termine une fonction équivaut à exécuter unereturn
instruction sans expression" (3.6.6.4). Cependant, dans c99, le comportement n'est pas défini (6.9.1).la source
return
déclarations explicites . Il ne couvre pas la chute de la fin d'une fonction sans renvoyer une valeur.