42 votes

Renvoyer une référence à une variable locale ou temporaire

Regardez le code ci-dessous. Je sais qu'il ne renvoie pas l'adresse de la variable locale, mais pourquoi il fonctionne toujours et assigne la variable i dans main() à '6' ? Comment se fait-il qu'il ne renvoie la valeur que si la variable a été retirée de la mémoire de la pile ?

#include <iostream>

int& foo()
{
    int i = 6;
    std::cout << &i << std::endl; //Prints the address of i before return
    return i;
}

int main()
{
    int i = foo();
    std::cout << i << std::endl; //Prints the value
    std::cout << &i << std::endl; //Prints the address of i after return
}

24voto

Marcelo Cantos Points 91211

Vous avez eu de la chance. Le retour de la fonction n'efface pas immédiatement le cadre de la pile dont vous venez de sortir.

BTW, comment avez-vous confirmé que vous avez reçu un 6 en retour ? L'expression std::cout << &i ... imprime l'adresse de i et non sa valeur.

3voto

NeuroScr Points 313

Ça doit être quelque chose que votre compilateur fait.

http://www.learncpp.com/cpp-tutorial/74a-returning-values-by-value-reference-and-address/

Confirme que votre exemple va supprimer la référence de la mémoire de la pile.

3voto

Björn Pollex Points 41424

Renvoyer une référence ou un pointeur à une variable locale est un comportement non défini. Un comportement indéfini signifie que la norme laisse la décision au compilateur. Cela signifie qu'un comportement non défini fonctionne parfois bien, et que parfois, ce n'est pas le cas .

2voto

John Dibling Points 56814

L'adresse de i ne va jamais changer dans main() mais la valeur qu'il contient le sera. Vous prenez la référence d'une variable locale et l'utilisez après que cette référence soit sortie de sa portée. (Avertissement de langage imprécis) La valeur 6 est sur la pile. Puisque vous n'avez rien fait avec la pile après avoir mis 6 la référence à ce dernier contiendra toujours la même valeur. Donc, comme d'autres l'ont dit, vous avez eu de la chance.

Pour voir à quel point vous êtes chanceux, essayez d'exécuter ce code qui utilise la pile après avoir appelé foo() :

#include <iostream>
#include <ctime>
#include <numeric>

int& foo()
{
    int i = 6;
    std::cout << &i << " = " << i << std::endl; //Prints the address of i before return
    return i;
}

long post_foo(int f)
{
    srand((unsigned)time(0));

    long vals[10] = {0};
    size_t num_vals = sizeof(vals)/sizeof(vals[0]);
    for( size_t i = 0; i < num_vals; ++i )
    {
        int r = (rand()%2)+1;
        vals[i] = (i+f)*r;
    }

    long accum = std::accumulate(vals, &vals[num_vals], 0);
    return accum * 2;
}

int main()
{
    int &i = foo();
//  std::cout << "post_foo() = " << post_foo(i) << std::endl;
    std::cout << &i << " = " << i << std::endl; 
}

Quand je l'ai lancé avec le post_foo() appel commenté, 6 était toujours sur la pile et la sortie était :

002CF6C8 = 6
002CF6C8 = 6

...mais quand j'ai dé-commenté l'appel à post_foo() et l'a refait, 6 était parti depuis longtemps :

001FFD38 = 6
post_foo() = 310
001FFD38 = 258923464

1voto

John Gordon Points 1326

Alors que votre fonction renvoie un entier par référence, il est immédiatement affecté à la variable locale 'i' dans main(). Cela signifie que la mémoire de la pile allouée à foo() doit persister juste assez longtemps pour l'affectation du retour. Bien que ce ne soit pas une bonne idée, cela fonctionne généralement. Si vous aviez essayé de garder une référence

int &i = foo();

il serait beaucoup plus susceptible d'échouer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X