Question

Hier, je me suis retrouvé à écrire un code comme celui-ci:

SomeStruct getSomeStruct()
{
    SomeStruct input;

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

Bien sûr, oublier de retourner la structure que je viens de créer. Curieusement, les valeurs de la structure que était 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 bien un autre SomeStruct a-t-il été créé et initialisé implicitement quelque part?

Était-ce utile?

La solution

  

Une autre SomeStruct a-t-elle été créée et initialisée de manière implicite?

Réfléchissez à la façon dont la structure est renvoyée. Si x et y sont tous deux 32 bits, il est trop gros pour tenir dans un registre sur une architecture 32 bits et il en va de même pour les valeurs 64 bits sur une Architecture 64 bits (la réponse de @Denton Gentry indique comment les valeurs plus simples sont renvoyées), il faut donc les allouer quelque part. Il serait inutile d'utiliser le tas pour cela, il doit donc être alloué sur la pile. Mais cela ne peut pas être sur le cadre de pile de votre fonction getSomeStruct , car elle n’est plus valide après le retour de la fonction.

Le compilateur demande à l'appelant d'indiquer à la fonction appelée où placer le résultat (probablement quelque part sur la pile de l'appelant), en lui transmettant un pointeur masqué sur l'espace qui lui est alloué. Ainsi, l'emplacement où il est défini sur zéro se trouve sur l'appelant , pas sur votre getSomeStruct .

Il existe également des optimisations telles que "l'optimisation du retour de la valeur nommée". où des copies supplémentaires peuvent être supprimé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 examiner la fonction d'appelant. S'agit-il d'initialisation (à zéro) d'un "vide"? SomeStruct auquel vous affecterez ultérieurement la valeur de retour de votre fonction getSomeStruct ? Ou fait-il autre chose?

Autres conseils

Le fait de perdre la fin d'une fonction déclarée comme retournant une valeur (sans retourner explicitement une valeur) a des conséquences indéterminées. Pour gcc, vous devez commencer par le commutateur de ligne de commande -Wall qui active les avertissements les plus utiles. L’avertissement spécifique de gcc qui contrôle l’avertissement souhaité est -Wreturn-type (inclus dans -Wall , je le mentionne pour des raisons d'exhaustivité).

Une fois les avertissements activés, vous devez également utiliser -Werror pour les traiter comme des erreurs et arrêter la construction au moment où elle détecte une erreur.

Les conventions d’appel de la plupart des architectures de 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 de valeur, l'appelant utilisera néanmoins les déchets de 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 divers dans la fonction. Ainsi, lorsque vous oubliez de spécifier une valeur de retour, il n'est pas rare de trouver la valeur correcte renvoyée miraculeusement à l'appelant: le compilateur a utilisé ce registre pour stocker votre objet.

Malheureusement, même une modification anodine de la fonction peut entraîner une modification de l'allocation de registre. La valeur renvoyée devient alors un vrai déchet.

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 (même depuis VC6):

    erreur C4716: 'getSomeStruct': doit renvoyer une valeur

  • Digital Mars:

    Avertissement 18: le retour implicite de getSomeStruct à la fermeture '}' ne renvoie pas de valeur

  • Comeau:

    avertissement: instruction de retour manquante à la fin de la fonction non-vide "getSomeStruct"

  • gcc:

    aucune erreur ni avertissement

Étant donné les quelques phrases suivantes de la norme (6.6.3, paragraphe 2):

  

Une déclaration return sans   expression ne peut être utilisée que dans   fonctions qui ne renvoient pas de valeur,   c'est-à-dire une fonction avec le retour   tapez void, un constructeur (12.1) ou un   destructeur (12.4). ... qui coule   la fin d'une fonction est équivalente à   un retour sans valeur; cela résulte   dans un comportement indéfini dans un   fonction de retour de valeur.

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

Pour moi, le compilateur ne l'a pas autorisé: http://codepad.org/KkzVCesh

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

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top