2016-03-23 34 views
8

Ho riscontrato un problema nel mio apprendimento del linguaggio C++, in cui una variabile locale in una funzione viene passata alla variabile locale con lo stesso nome in un'altra funzione, entrambe queste funzioni vengono eseguite in main().Variabili locali passate (C++)

Quando questo viene eseguito,

#include <iostream> 
using namespace std; 

void next(); 
void again(); 

int main() 
{ 
    int a = 2; 
    cout << a << endl; 
    next(); 
    again(); 
    return 0; 
} 

void next() 
{ 
    int a = 5; 
    cout << a << endl; 
} 

void again() 
{ 
    int a; 
    cout << a << endl; 
} 

essa stampa:

2 
5 
5 

Mi aspettavo che ancora una volta() sarebbe dire nullo o 0 dal momento che 'a' è dichiarato ancora una volta lì, eppure sembra per usare il valore che 'a' è stato assegnato in next().

Perché next() passa il valore della variabile locale 'a' a again() se 'a' viene dichiarato un'altra volta in again()?

+3

Nulla passato. 'a' in' again() 'non è stato inizializzato, qualsiasi valore è possibile. – songyuanyao

+0

Concordato quindi il valore di 'a' in è un comportamento indefinito. – drescherjm

risposta

7

http://en.cppreference.com/w/cpp/language/ub

Hai ragione, una variabile non inizializzata è un no-no. Tuttavia, è consentito dichiarare una variabile e non inizializzarla fino a un momento successivo. La memoria è messa da parte per contenere il numero intero, ma quale valore si trova in quella memoria fino a quando non lo si può fare. Alcuni compilatori autoinizializzeranno le variabili a valori indesiderati (per aiutarti a catturare i bug), alcuni si auto-inizializzeranno ai valori predefiniti e alcuni non farebbero nulla. Lo stesso C++ non promette nulla, quindi è un comportamento indefinito. Nel tuo caso, con il tuo semplice programma, è abbastanza facile immaginare come il compilatore abbia creato il codice assembly che ha riutilizzato esattamente lo stesso pezzo di memoria senza alterarlo. Tuttavia, è una fortuna cieca, e anche nel tuo semplice programma non è garantito che accada. Questi tipi di bug possono essere effettivamente abbastanza insidiosi, quindi rendi una regola: stai attento alle variabili non inizializzate.

1

Questo è un comportamento completamente casuale e non definito.

Quello che è successo è che avete due funzioni chiamate una dopo l'altra. Entrambi avranno prologi di funzione più o meno identici e entrambi riserva una variabile esattamente della stessa dimensione in pila.

Dato che non ci sono altre variabili in gioco e lo stack non viene modificato tra le chiamate, si finisce con la variabile locale nella seconda funzione "atterraggio" nella stessa posizione della variabile locale della funzione precedente.

Chiaramente, questo non è buono su cui fare affidamento. In effetti, è un perfetto esempio del perché si dovrebbe sempre inizializzare le variabili!

5

Un non inizializzato non static variabile locale di * tipo built-in (uff! Che era un boccone) ha un valore indeterminato . Tranne i tipi char, l'utilizzo di tale valore produce formalmente Comportamento non definito, a.k.a. UB. Tutto può succedere, compreso il comportamento che vedi.

A quanto pare con il compilatore e le opzioni, l'area di stack che è stato utilizzato per a nella chiamata di next, non è stato utilizzato per qualcos'altro, fino alla chiamata di again, dove è stato riutilizzato per la a in again, ora con il lo stesso valore di prima.

Ma non puoi fidarti di quello. Con UB può accadere qualcosa, o niente.


* o più in generale di tipo POD, Plain Old dati. Le specifiche dello standard di questo sono alquanto complicate. In C++ 11 inizia con §8.5/11, "Se non viene specificato alcun inizializzatore per un oggetto, l'oggetto viene inizializzato di default; se non viene eseguita alcuna inizializzazione, un oggetto con durata di archiviazione automatica o dinamica ha un valore indeterminato. ". Dove "automatico & hellip; durata di archiviazione "include il caso di variabile locale non static. E dove la "non inizializzazione" può avvenire in due modi tramite §8.5/6 che definisce l'inizializzazione di default, vale a dire tramite un costruttore predefinito do-nothing o tramite l'oggetto che non è di tipo classe o array.