2010-04-30 10 views
40

Sehen Sie sich den folgenden Code an. Ich weiß, dass es die Adresse der lokalen Variablen nicht zurückgibt, aber warum funktioniert es immer noch und weist die Variable i in main() zu '6' zu? Wie gibt es nur den Wert zurück, wenn die Variable aus dem Stapelspeicher entfernt wurde?Zurückgeben eines Verweises auf eine lokale oder temporäre Variable

#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 
} 
+19

Sie haben nur Glück. Tu es nicht. –

+3

Sie können dies nützlich finden: http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope – letsc

+0

Ich glaube, etwas Glück ist in der Tatsache, dass ich bin unverändert in foo() (was Compilern erlaubt, in Text oder irgendwo langlebig anstelle von Stack zu platzieren) – mho

Antwort

22

Sie hatten Glück. Die Rückkehr von der Funktion löscht nicht sofort den Stapelrahmen, den Sie gerade verlassen haben.

BTW, wie hast du bestätigt, dass du eine 6 zurück hast? Der Ausdruck std::cout << &i ... gibt die Adresse i aus, nicht ihren Wert.

+3

die 6 kommt heraus, wenn er es in main() druckt. –

+2

@San: Nun, es ist jetzt, dass @ Dave18 die Frage bearbeitet. –

+1

Entschuldigung, ich muss die Frage danach gelesen haben :) –

1

Während Ihre Funktion eine Ganzzahl als Referenz zurückgibt, wird sie sofort der lokalen Variablen 'i' in main() zugewiesen. Das bedeutet, dass der Stack-Speicher, der für foo() reserviert ist, nur so lange bestehen muss, bis die Rückgabe zugewiesen wurde. Während es schlechte Form ist, funktioniert das normalerweise. Wenn Sie versucht hätten, eine Referenz

int &i = foo(); 

zu behalten, wäre es viel wahrscheinlicher zu scheitern.

3

Die Rückgabe des Verweises oder Zeigers auf eine lokale Variable ist ein nicht definiertes Verhalten. Undefiniertes Verhalten bedeutet, dass der Standard die Entscheidung dem Compiler überlässt. Das bedeutet, ein undefiniertes Verhalten funktioniert manchmal gut und sometimes it doesn't.

+4

Und die Chancen, dass es nicht gut funktioniert, hängen von der Wichtigkeit der Leute ab, die das Demo anschauen. – KeithB

2

Die Adresse i wird nie in main() ändern, aber der darin enthaltene Wert wird. Sie nehmen die Referenz einer lokalen Variablen und verwenden sie, nachdem diese Referenz außerhalb des Gültigkeitsbereichs liegt. (Unpräzise Sprachwarnung) Der Wert 6 ist auf dem Stapel. Da Sie nichts mit dem Stack getan haben, nachdem Sie dort 6 eingegeben haben, enthält der Verweis darauf immer noch denselben Wert. Wie andere gesagt haben, haben Sie Glück gehabt.

Um zu sehen, wie glücklich, versuchen Sie diesen Code ausgeführt wird, die den Stapel verwendet, nachdem Sie foo() nennen:

#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; 
} 

Wenn ich dies mit dem post_foo() Anruf lief auf Kommentar, 6 war noch auf dem Stapel und dem Ausgang war:

002CF6C8 = 6 
002CF6C8 = 6 

... aber wenn ich den Anruf zu post_foo() und lief wieder es unkommentiert wurde 6 lange vorbei:

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