Renvoyer une référence à une variable locale ou temporaire [dupliquer]

Cette question a déjà une réponse ici:

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

#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
}
40
c++
demandé sur juanchopanza 2010-04-30 15:52:21

5 réponses

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

BTW, comment avez-vous confirmé que vous avez eu un 6 Retour? L'expression std::cout << &i ... imprime l'adresse de i, pas sa valeur.

22
répondu Marcelo Cantos 2010-04-30 11:54:17

Doit être quelque chose que fait votre compilateur.

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

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

3
répondu NeuroScr 2010-04-30 11:57:35

Renvoyer une référence ou un pointeur vers une variable locale est un comportement indéfini. Un comportement indéfini signifie que la norme laisse la décision au compilateur. Cela signifie que le comportement indéfini fonctionne parfois bien, et parfois, il ne fonctionne pas .

3
répondu Björn Pollex 2017-05-23 12:07:11

L'adresse de i ne changera jamais dans main(), mais la valeur qui y est contenue le fera. Vous prenez la référence d'une variable locale et l'utilisez après que cette référence est tombée hors de portée. (Avertissement de langue imprécise) la valeur 6 est sur la pile. Puisque vous n'avez rien fait avec la pile après avoir mis 6 là, la référence à celle-ci contiendra toujours la même valeur. Donc, comme d'autres l'ont dit, vous avez eu de la chance.

Pour voir à quel point la chance, 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 j'ai couru ceci avec l'appel post_foo() commenté, 6 était toujours sur la pile et la sortie était:

002CF6C8 = 6
002CF6C8 = 6

...mais quand j'ai désapprouvé l'appel à post_foo() et l'ai relancé, 6 était parti depuis longtemps:

001FFD38 = 6
post_foo() = 310
001FFD38 = 258923464
2
répondu John Dibling 2010-04-30 14:24:02

Alors que votre fonction renvoie un entier par référence, elle est immédiatement affectée à la variable locale 'i' dans main (). Cela signifie que la mémoire de la pile allouée pour foo() doit persister juste assez longtemps pour l'affectation de retour. Bien que ce soit une mauvaise forme, 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.

1
répondu John Gordon 2010-04-30 12:30:02