Come vengono memorizzati i nomi delle variabili in C?

In C, supponiamo tu abbia una variabile chiamata variable_name . Diciamo che si trova a 0xaaaaaaaa , e in quell’indirizzo di memoria hai il numero intero 123. Quindi, in altre parole, variable_name contiene 123.

Sto cercando chiarimenti sul fraseggio ” variable_name si trova a 0xaaaaaaaa “. Come riconosce il compilatore che la stringa “nome_variabile” è associata a quel particolare indirizzo di memoria? La stringa “variable_name” è memorizzata da qualche parte nella memoria? Il compilatore sostituisce semplicemente variable_name per 0xaaaaaaaa ogni volta che lo vede, e in caso affermativo, non dovrebbe usare la memoria per fare quella sostituzione?

I nomi delle variabili non esistono più dopo l’esecuzione del compilatore (escludendo casi speciali come i globali esportati nelle librerie condivise o nei simboli di debug). L’intero atto di compilazione è inteso a prendere quei nomi simbolici e algoritmi rappresentati dal codice sorgente e trasformarli in istruzioni macchina native. Quindi sì, se si dispone di un 0xaaaaaaaa globale, e il compilatore e il linker decidono di inserirlo in 0xaaaaaaaa , quindi, ovunque sia utilizzato nel codice, sarà accessibile tramite tale indirizzo.

Quindi, per rispondere alle tue domande letterali:

Come riconosce il compilatore che la stringa “nome_variabile” è associata a quel particolare indirizzo di memoria?

La toolchain (compilatore e linker) lavora insieme per assegnare una posizione di memoria per la variabile. È compito del compilatore tenere traccia di tutti i riferimenti e il linker inserisce gli indirizzi giusti in seguito.

La stringa "variable_name" memorizzata da qualche parte nella memoria?

Solo mentre il compilatore è in esecuzione.

Il compilatore sostituisce semplicemente variable_name per 0xaaaaaaaa ogni volta che lo vede, e in caso affermativo, non dovrebbe usare la memoria per fare quella sostituzione?

Sì, è più o meno quello che succede, tranne che è un lavoro a due stadi con il linker. E sì, usa la memoria, ma è la memoria del compilatore , non nulla in fase di esecuzione per il tuo programma.

Un esempio potrebbe aiutarti a capire. Proviamo questo programma:

 int x = 12; int main(void) { return x; } 

Piuttosto semplice, vero? OK. Prendiamo questo programma, lo compiliamo e guardiamo allo sassembly:

 $ cc -Wall -Werror -Wextra -O3 example.c -o example $ otool -tV example example: (__TEXT,__text) section _main: 0000000100000f60 pushq %rbp 0000000100000f61 movq %rsp,%rbp 0000000100000f64 movl 0x00000096(%rip),%eax 0000000100000f6a popq %rbp 0000000100000f6b ret 

Vedi quella linea? Sta prendendo la variabile globale (in un modo relativo a un indicatore di istruzioni, in questo caso). Non c’è più menzione di x .

Ora rendiamola un po ‘più complicata e aggiungi una variabile locale:

 int x = 12; int main(void) { volatile int y = 4; return x + y; } 

Lo sassembly per questo programma è:

 (__TEXT,__text) section _main: 0000000100000f60 pushq %rbp 0000000100000f61 movq %rsp,%rbp 0000000100000f64 movl $0x00000004,0xfc(%rbp) 0000000100000f6b movl 0x0000008f(%rip),%eax 0000000100000f71 addl 0xfc(%rbp),%eax 0000000100000f74 popq %rbp 0000000100000f75 ret 

Ora ci sono due istruzioni movl e un’istruzione movl . Puoi vedere che il primo movl sta inizializzando y , che è deciso sarà in pila (puntatore base – 4). Quindi il prossimo movl ottiene la x globale in un registro eax , e l’ addl aggiunge y a quel valore. Ma come puoi vedere, le stringhe letterali y non esistono più. Erano convenienze per te , il programmatore, ma il computer di certo non si preoccupa di loro al momento dell’esecuzione.

Il compilatore AC crea innanzitutto una tabella dei simboli, che memorizza la relazione tra il nome della variabile e il punto in cui si trova nella memoria. Durante la compilazione, utilizza questa tabella per sostituire tutte le istanze della variabile con una posizione di memoria specifica, come altri hanno affermato. Puoi trovare molto di più sulla pagina di Wikipedia.

Tutte le variabili sono sostituite dal compilatore. Per prima cosa vengono sostituiti con riferimenti e in seguito il linker posiziona gli indirizzi invece dei riferimenti.

In altre parole. I nomi delle variabili non sono più disponibili non appena è stato eseguito il compilatore

Questo è ciò che viene definito un dettaglio di implementazione . Mentre quello che descrivi è il caso in tutti i compilatori che ho mai usato, non è necessario che sia il caso. Il compilatore AC può inserire tutte le variabili in un hashtable e cercarle in fase di runtime (o qualcosa del genere) e in effetti i primi interpreti JavaScript hanno fatto esattamente questo (ora fanno compilazione Just-In-TIme che risulta in qualcosa di molto più grezzo).

Specificamente per compilatori comuni come VC ++, GCC e LLVM: il compilatore generalmente assegnerà una variabile a una posizione in memoria. Le variabili di ambito globale o statico ottengono un indirizzo fisso che non cambia mentre il programma è in esecuzione, mentre le variabili all’interno di una funzione ottengono un indirizzo di stack , ovvero un indirizzo relativo al puntatore dello stack corrente, che cambia ogni volta che una funzione è chiamato. (Questa è una semplificazione eccessiva.) Gli indirizzi di stack diventano non validi non appena la funzione ritorna, ma hanno il vantaggio di avere effettivamente zero spese generali da utilizzare.

Una volta che a una variabile è stato assegnato un indirizzo, non c’è più bisogno del nome della variabile, quindi viene scartata. A seconda del tipo di nome, il nome può essere scartato in fase di preelaborazione (per i nomi macro), tempo di compilazione (per variabili / funzioni statiche e locali) e tempo di collegamento (per variabili / funzioni globali). Se viene esportato un simbolo ( reso visibile ad altri programmi in modo che possano accedervi), il nome di solito rimane da qualche parte in una “tabella dei simboli” che occupa una quantità insignificante di memoria e spazio su disco.

Il compilatore sostituisce semplicemente variabile_name per 0xaaaaaaaa ogni volta che lo vede

Sì.

e se sì, non dovrebbe usare la memoria per fare quella sostituzione?

Sì. Ma è il compilatore, dopo aver compilato il tuo codice, perché ti importa della memoria?