Cosa succede quando una variabile esce dal campo di applicazione?

Nella maggior parte dei linguaggi gestiti (ovvero quelli con un GC), le variabili locali che escono dall’ambito sono inaccessibili e hanno una priorità GC più alta (quindi, saranno prima liberate).

Ora, C non è un linguaggio gestito, cosa succede alle variabili che vanno fuori ambito qui?

Ho creato un piccolo test-case in C:

#include  int main(void){ int *ptr; { // New scope int tmp = 17; ptr = &tmp; // Just to see if the memory is cleared } //printf("tmp = %d", tmp); // Compile-time error (as expected) printf("ptr = %d\n", *ptr); return 0; } 

Sto usando GCC 4.7.3 per compilare e il programma sopra stampa 17 , perché? E quando / in quali circostanze saranno liberate le variabili locali?

Il comportamento effettivo del tuo esempio di codice è determinato da due fattori principali: 1) il comportamento non è definito dal linguaggio, 2) un compilatore ottimizzante genererà codice macchina che non corrisponde fisicamente al tuo codice C.

Ad esempio, nonostante il fatto che il comportamento non sia definito, GCC può (e lo farà) facilmente ottimizzare il tuo codice a un mero

 printf("ptr = %d\n", 17); 

il che significa che l’output che vedi ha ben poco a che fare con ciò che accade a qualsiasi variabile nel tuo codice.

Se desideri che il comportamento del tuo codice rifletta meglio ciò che accade fisicamente, dovresti dichiarare volatile tuoi indicatori. Il comportamento sarà ancora indefinito, ma almeno limiterà alcune ottimizzazioni.

Ora, per quanto riguarda cosa succede alle variabili locali quando escono dall’ambito di applicazione. Non succede nulla di fisico Un’implementazione tipica allocerà spazio sufficiente nello stack del programma per memorizzare tutte le variabili al livello più profondo di nidificazione dei blocchi nella funzione corrente. Questo spazio viene tipicamente allocato nello stack in un colpo all’avvio della funzione e rilasciato all’uscita della funzione.

Ciò significa che la memoria precedentemente occupata da tmp continua a rimanere riservata nello stack fino alla chiusura della funzione. Ciò significa anche che lo stesso spazio di stack può (e sarà) riutilizzato da diverse variabili aventi approssimativamente lo stesso livello di “profondità di localizzazione” nei blocchi fratelli. Lo spazio manterrà il valore dell’ultima variabile finché un’altra variabile dichiarata in una variabile di blocco fratello non lo sovrascrive. Nel tuo esempio nessuno sovrascrive lo spazio precedentemente occupato da tmp , quindi generalmente vedrai il valore 17 sopravvivere intatto in quella memoria.

Tuttavia, se lo fai

 int main(void) { volatile int *ptr; volatile int *ptrd; { // Block int tmp = 17; ptr = &tmp; // Just to see if the memory is cleared } { // Sibling block int d = 5; ptrd = &d; } printf("ptr = %d %d\n", *ptr, *ptrd); printf("%p %p\n", ptr, ptrd); } 

vedrete che lo spazio precedentemente occupato da tmp è stato riutilizzato per d e il suo valore precedente è stato cancellato. Il secondo printf tipicamente produrrà lo stesso valore del puntatore per entrambi i puntatori.

La durata di un object automatico termina alla fine del blocco in cui è dichiarata.

L’accesso a un object al di fuori della sua durata è un comportamento non definito in C.

(C99, 6.2.4p2) “Se si fa riferimento a un object al di fuori della sua durata, il comportamento non è definito. Il valore di un puntatore diventa indeterminato quando l’object a cui punta raggiunge la fine della sua durata.”

Le variabili locali sono allocate nello stack. Non vengono “liberati” nel senso in cui si pensa ai linguaggi GC o alla memoria allocata nell’heap. Esse semplicemente escono dal campo di applicazione e per i tipi di codice incorporato il codice non farà nulla e per gli oggetti viene chiamato il distruttore.

Accedervi oltre il loro ambito è un comportamento indefinito. Sei stato fortunato, dato che nessun altro codice ha sovrascritto quell’area di memoria … ancora.