2008-11-16 5 views
14

Hier, je me suis retrouvé à écrire du code comme ceci:Que se passe-t-il si vous ne renvoyez pas de valeur en C++?

SomeStruct getSomeStruct() 
{ 
    SomeStruct input; 

    cin >> input.x; 
    cin >> input.y; 
} 

Bien sûr oublier de revenir en fait le struct que je viens de créer. Bizarrement, les valeurs dans la structure renvoyées par cette fonction ont été initialisées à zéro (lorsqu'elles ont été compilées avec g ++). Est-ce juste une coïncidence ou est-ce qu'un autre SomeStruct a été créé et initialisé quelque part implicitement?

Répondre

7

Est-ce qu'un autre SomeStruct a été créé et initialisé quelque part implicitement?

Pensez à la manière dont la structure est renvoyée. Si les deux x et y sont 32 bits, il est trop grand pour tenir dans un registre sur une architecture 32 bits, et la même chose s'applique aux valeurs 64 bits sur une architecture 64 bits (la réponse de @Denton Gentry mentionne comment les valeurs sont plus simples retourné), il doit donc être attribué quelque part. Il serait inutile d'utiliser le tas pour cela, donc il doit être alloué sur la pile. Mais il ne peut pas être sur le cadre de la pile de votre fonction getSomeStruct, car cela n'est plus valide après le retour de la fonction.

Le compilateur a plutôt l'appelant dit à la fonction appelée où placer le résultat (qui est probablement quelque part sur la pile de l'appelant), en passant la fonction appelée un pointeur caché à l'espace qui lui est alloué. Ainsi, l'endroit où il est mis à zéro est sur l'appelant, pas sur votre getSomeStruct fonction.

Il existe également des optimisations telles que «l'optimisation du retour de la valeur nommée», dans laquelle des copies supplémentaires peuvent être éli- minées.Donc, si vous aviez utilisé le return manquant, le résultat serait créé directement sur l'espace alloué par l'appelant, au lieu de créer un temporaire et de le copier.

Pour en savoir plus sur ce qui se passe, vous devez regarder la fonction de l'appelant. Initialise-t-il (à zéro) un SomeStruct "vide" auquel vous affectez plus tard la valeur de retour de votre fonction getSomeStruct? Ou fait-il quelque chose d'autre?

2

Pour moi, le compilateur ne lui permettait pas: http://codepad.org/KkzVCesh

+0

Il semblerait que cela ne l'ait pas permis car votre compilateur a été configuré pour que les avertissements soient traités comme des erreurs. Pensez-vous que cela pourrait être le cas d'avoir un niveau d'avertissement trop bas quelque part? –

20

tomber à la fin d'une fonction qui est déclarée renvoyer une valeur (sans revenir explicitement une valeur) entraîne des conséquences non définies. Pour gcc, vous devez commencer par le commutateur de ligne de commande -Wall qui active la plupart des avertissements utiles. L'avertissement gcc spécifique qui contrôle l'avertissement que vous voulez est -Wreturn-type (qui est inclus dans -Wall, je mentionne juste ceci pour l'exhaustivité). Une fois les avertissements activés, vous devez également utiliser -Werror pour traiter les avertissements comme des erreurs et arrêter la génération au point où elle détecte une erreur.

+1

J'ai toujours été étonné que ce soit simplement un avertissement au lieu d'une erreur. Ça fait beaucoup plus mal que ça aide. –

+0

Il était légal de ne rien retourner en C. Il était encore indéfini, mais au moins c'était légal. – Jonathan

+0

En particulier, si j'oublie de retourner un objet 'std :: vector' ou un autre objet qui gère une mémoire de tas, l'objet restera dans un état invalide et ne saura donc pas comment se détruire quand le moment viendra. Cela rend une session de débogage difficile. – Bernard

8

Les conventions d'appel de la plupart des architectures CPU modernes spécifient un registre particulier pour renvoyer une valeur de retour de fonction à l'appelant. L'appelant appelle la fonction, puis utilise le registre spécifié comme valeur de retour. Si vous ne renvoyez pas explicitement une valeur, l'appelant utilisera néanmoins les déchets qui se trouvent dans ce registre.

Le compilateur utilisera également tous les registres dont il dispose pour le calcul interne dans la fonction. Le registre désigné pour contenir la valeur de retour sera également utilisé pour le calcul de misc dans la fonction. Ainsi, lorsque vous oubliez de spécifier une valeur de retour, il n'est pas rare de trouver que la valeur correcte a été renvoyée miraculeusement à l'appelant: le compilateur a utilisé ce registre pour stocker votre objet.

Malheureusement, même un changement trivial de la fonction peut entraîner une modification de l'allocation de registre, de sorte que la valeur de retour devient une véritable poubelle.

1

Vous n'avez reçu aucun avertissement car vous n'aviez pas -Wall -Werror activé. (Comme indiqué dans les autres réponses)

Cependant, je pense que vous avez probablement obtenu une structure remplie de zéros parce que l'objet de la pile a été construit par défaut dans la fonction appelant, éventuellement avec des arguments zéro explicites, ou bien en raison de zéros sur le empiler?

3

Je trouve cela intéressant. Avec les options par défaut utilisées, les compilateurs suivants ont le comportement suivant lors de la compilation de la fonction GetSomeStruct():

  • Microsoft VC, toutes les versions (depuis VC6 de toute façon):

    error C4716: 'getSomeStruct' : must return a value

  • Digital Mars :

    Warning 18: implied return of getSomeStruct at closing '}' does not return value

  • Comeau:

    warning: missing return statement at end of non-void function "getSomeStruct"

  • gcc:

    pas d'erreur ou d'avertissement

Compte tenu du couple suivant des phrases de la norme (6.6. 3 Paragraphe 2):

Une instruction de retour sans expression peut être utilisé que dans fonctions qui ne retournent une valeur, qui est une fonction avec le vide de type retour , un constructeur (12.1), ou un destructor (12.4). ... s'écoulant la fin d'une fonction est équivalente à un retour sans valeur; il en résulte dans un comportement indéfini dans une fonction de retour de valeur .

Je dirais qu'il y a peu de raison pour qu'un compilateur ne donne pas d'erreur dans ce cas. Pourquoi tant de compilateurs ne donnent-ils qu'un avertissement ou aucun diagnostic?

+0

Je m'objecte à l'affirmation que GCC ne met pas en garde à ce sujet. Veuillez lancer gcc avec l'option '-Wall', et vous obtiendrez:' warning: aucune instruction return dans la fonction retournant non-void [-Wreturn-type] ' – Loomchild

Questions connexes