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
}
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.
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.
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 .
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
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.