2012-02-08 10 views
8

Cosa significa la seguente affermazione?Perché il compilatore non conosce gli indirizzi delle variabili locali in fase di compilazione?

variabili locali e allocati dinamicamente avere indirizzi che non sono conosciuti dal compilatore quando il file sorgente viene compilato

Ho usato pensare che le variabili locali sono assegnati gli indirizzi in fase di compilazione, ma questo indirizzo può cambia quando uscirà dall'ambito e quindi rientrerà nell'ambito durante la chiamata di funzione. Ma l'affermazione precedente dice che l'address delle variabili locali non è nota al compilatore. Quindi come vengono allocate le variabili locali? Perché gli indirizzi delle variabili globali possono essere conosciuti al momento della compilazione ??

Inoltre, è possibile fornire un collegamento valido per leggere come vengono allocate le variabili locali e altre?

Grazie in anticipo!

+5

In un certo senso, nessun indirizzo è noto fino a quando il sistema operativo carica il programma e imposta i mapping della memoria virtuale nella MMU. Il compilatore conosce il layout, cioè l'ordine in cui appaiono le cose, ma tutto è misurato da un indirizzo di base. Per la durata dell'archiviazione statica, viene misurata dall'indirizzo di caricamento del file binario. Per la durata della memorizzazione automatica, viene misurata dal puntatore dello stack. E l'archiviazione dinamica ha un indirizzo determinato in fase di esecuzione, ma i membri vengono archiviati in un offset dall'indirizzo dinamico. –

+0

@ Ben: questo è un problema completamente separato. –

risposta

13

La citazione sopra riportata è corretta: il compilatore in genere non conosce l'indirizzo delle variabili locali in fase di compilazione. Detto questo, il compilatore probabilmente conosce l'offset dalla base del frame dello stack a cui verrà localizzata una variabile locale, ma a seconda della profondità dello stack di chiamata, che potrebbe tradursi in un indirizzo diverso in fase di esecuzione. A titolo di esempio, si consideri questo codice ricorsivo (che, tra l'altro, non è affatto buono codice!):

int Factorial(int num) { 
    int result; 
    if (num == 0) 
     result = 1; 
    else 
     result = num * Factorial(num - 1); 

    return result; 
} 

A seconda del parametro num, questo codice potrebbe finire per fare diverse chiamate ricorsive, quindi non c'è saranno più copie di result in memoria, ognuna con un valore diverso. Di conseguenza, il compilatore non può sapere dove andranno tutti. Tuttavia, ogni istanza di result sarà probabilmente sfalsata della stessa quantità dalla base del frame dello stack contenente ciascuna invocazione Factorial, anche se in teoria il compilatore potrebbe eseguire altre operazioni come l'ottimizzazione di questo codice in modo che sia presente una sola copia di result.

In genere, i compilatori allocano le variabili locali mantenendo un modello del frame dello stack e del tracciamento in cui si trova la successiva posizione libera nello stack frame. In questo modo, le variabili locali possono essere allocate rispetto all'inizio del frame dello stack e, quando viene chiamata la funzione, è possibile utilizzare quell'indirizzo relativo, in combinazione con l'indirizzo dello stack, per cercare la posizione di tale variabile nello specifico frame dello stack .

Le variabili globali, d'altra parte, possono avere i loro indirizzi noti in fase di compilazione. Differiscono dai locali principalmente in quanto vi è sempre una copia di una variabile globale in un programma. Le variabili locali possono esistere 0 o più volte a seconda di come va l'esecuzione. Come risultato del fatto che esiste una copia univoca del globale, il compilatore può inserire un indirizzo in hardcode per esso.

Per quanto riguarda ulteriori letture, se si desidera un trattamento abbastanza approfondita di come un compilatore può lay out variabili, si consiglia di prendere una copia di Compilers: Principles, Techniques, and Tools, Second Edition da Aho, Lam, Sethi, e Ullman . Sebbene gran parte di questo libro riguardi altre tecniche di costruzione di compilatori, un'ampia sezione del libro è dedicata all'implementazione della generazione di codice e alle ottimizzazioni che possono essere utilizzate per migliorare il codice generato.

Spero che questo aiuti!

+5

* automatico *, non * locale * (che potrebbe essere 'statico') –

+0

@ BenVoigt- Sono d'accordo. Ho evitato di usare il termine perché molti programmatori iniziano ad apprendere "locale" e "globale" in contrapposizione a "automatico", "statico" e "dinamico", poiché i termini precedenti sono più generali e questi ultimi si applicano in genere solo a C/C++ . – templatetypedef

+0

@templatetypedef: penso di averti, ma potresti indicare errore nella risposta dell'utente966379. Ha detto che "Al momento della compilazione, il codice sorgente viene convertito in codice lingua macchina in modo che nessun indirizzo sia noto". sopra . –

0
  1. L'indirizzo di variabili dinamiche non è noto per il motivo previsto, poiché vengono allocati dinamicamente dal pool di memoria.
  2. L'indirizzo delle variabili locali non è noto poiché risiede nell'area di memoria "stack" . Lo svolgimento/riavvolgimento dello stack di un programma può posticipare in base alle condizioni di runtime del flusso di codice.

Ad esempio:

void bar(); // forward declare 
void foo() 
{ 
    int i; // 'i' comes before 'j' 
    bar(); 
} 
void bar() 
{ 
    int j; // 'j' comes before 'i' 
    foo(); 
} 
int main() 
{ 
    if(...) 
    foo(); 
    else 
    bar(); 
} 

La condizione if può essere true o false e il risultato è noto solo in fase di esecuzione. Sulla base di quello int i o int j si svolgerà all'offset appropriato in pila.

+0

Penso che tu risponda che "l'indirizzo della variabile locale non è noto al momento della compilazione" .ma ho chiesto perché l'indirizzo non è conosciuto dal compilatore stesso. –

1

A mio parere la dichiarazione non parla di accesso alla fase di esecuzione variabili o scoping, ma sta cercando di dire qualcosa di più sottile.

La chiave qui è che il suo "locale e allocata dinamicamente" e "tempo di compilazione". Credo che quello che la dichiarazione sta dicendo è che quegli indirizzi non possono essere usati come costanti di tempo di compilazione. Questo è in contrasto con l'indirizzo delle variabili allocate staticamente, che possono essere utilizzate come costanti di tempo di compilazione. Un esempio di questo è nei modelli:

template<int *> 
class Klass 
{ 
}; 

int x; 

//OK as it uses address of a static variable; 
Klass<&::x> x_klass; 


int main() 
{ 
    int y; 
    Klass<&y> y_klass; //NOT OK since y is local. 
} 

Sembra che ci siano alcuni vincoli aggiuntivi su modelli che non consentono questo per compilare:

int main() 
{ 
    static int y; 
    Klass<&y> y_klass; 
} 

Tuttavia altri contesti che utilizzano la compilazione costanti di tempo possono essere in grado di utilizzare &y.

E allo stesso modo mi aspetto che questo sia non valida:

static int * p; 

int main() 
{ 
    p = new int(); 
    Klass<p> p_klass; 
} 

Poiché i dati di p è ora allocata dinamicamente (anche se p è statico).

+0

'Klass <&y>' nel secondo snippet è legale, molto probabilmente il compilatore non sta implementando completamente lo standard. "Un argomento * template * per un parametro template non-type, non-template * deve essere uno di:" ... "un'espressione costante (5.19) che designa l'indirizzo di un oggetto con durata di memorizzazione statica ed esterna o linkage interno o "... (sezione 14.3.2' [temp.arg.nontype] ') Questo è un cambiamento significativo da C++ 03, che richiedeva il collegamento esterno. –

+0

@BenVoigt Sono stato compilato in modalità C++ 03, penso. Sono contento di vedere che alcune delle restrizioni sui modelli sono state ridotte in C++ 11. –

0

È una bella domanda.

Durante l'esecuzione del codice, il programma viene caricato in memoria. Quindi la variabile locale ottiene l'indirizzo. In fase di compilazione, il codice sorgente viene convertito in codice lingua macchina in modo che possa essere eseguito

Problemi correlati