2009-11-03 13 views
8

in C, ho questo pezzo di codice:dove è rvalore memorizzato in c?

int a; 
a = 10 + 5 - 3 

voglio chiedere: dove è (10 + 5-3) conservato a? (Per quanto ne so, a si trova sulla pila, come su (10+5-3)? Come funziona questo rvalue ottenere calcolato?)

+7

È male che trovo "codice pezzo" esilarante? :) –

+0

un bel riferimento SO: http: // stackoverflow.it/questions/79923/what-and-where-are-the-stack-and-heap – Derek

+0

@eJames, può essere o non essere cattivo. Sospetto che la codetta non sia più in uso e molte persone potrebbero non sapere nemmeno di cosa si tratta (a meno che non abbiano guardato "BlackAdder"). – paxdiablo

risposta

4

In genere, il rvalue viene "immagazzinata" all'interno del programma stesso.

In altre parole, il compilatore stesso (prima che il programma è sempre eseguito) calcola il + 5 10 - valore 3 (che può fare così poiché in quanto è tutto basato su valori immediati costanti), ed emette la codice assembly per memorizzare il risultato di questo calcolo in qualunque valore-l per l'assegnazione (in questo caso, la variabile denominata a, che il compilatore probabilmente conosce come indirizzo relativo a un'origine di segmenti di dati).

Il valore di R, che ha un valore di 12 è quindi trovato solo all'interno del binario del programma, all'interno di un'istruzioni di montaggio che sembra come

mov <some dest, typically DS-relative>, $0C 

$ 0C è la "r-value ".

Se il valore r è il risultato di un calcolo che può essere eseguito solo in fase di esecuzione, dire se il codice c sottostante era: a = 17 * x; // x qualche runtime var, il valore r verrebbe anch'esso "memorizzato" (o meglio materializzato) come una serie di istruzioni all'interno del programma binario. La differenza con il semplice "mov dest, imm" sopra è che ci vorrebbero diverse istruzioni per caricare la variabile x in un accumulatore, moltiplicare per 17 e memorizzare il risultato all'indirizzo dove si trova la variabile a. È possibile che il compilatore può "autorizzare stessa" ;-) usare la pila per qualche risultato ecc intermedio ma tale sarebbe
a) completamente compilatore dipendente
b) transiant
c) e tipicamente occorrerebbe solo parte del valore r
è quindi sicuro dire che il valore r è un concetto in fase di compilazione che è incapsulato in parti del programma (non i dati) e non è memorizzato da nessuna parte ma nel programma binario.

In risposta a paxdiablo: la spiegazione offerta di cui sopra è davvero restrittiva delle possibilità perché lo standard C non in modo efficace non dettare qualcosa del genere. Nondimeno, la maggior parte di qualsiasi valore r viene alla fine materializzato, almeno in parte, da alcune istruzioni che impostano le cose in modo che il valore corretto, calcolato (in fase di esecuzione) o immediato, venga indirizzato correttamente.

+0

Penso che stia chiedendo dove sono le diverse parti del codice situate nello stack. – Derek

+1

* Niente * nello standard C richiede questo comportamento. Dovresti dire: "In genere, il valore r è ...". – paxdiablo

+0

@paxdiablo. Concordato e modificato con "Tipicamente", ho anche aggiunto un piccolo paragrafo in fondo, sei d'accordo? – mjv

1

Questo dipende dal compilatore. Di solito il valore (12) sarà calcolato dal compilatore. Viene quindi memorizzato nel codice, tipicamente come parte di un carico/spostamento di istruzioni di assemblaggio immediato.

+0

Quindi vuoi dire (10 + 5-3) è calcolato durante la compilazione? Mi chiedo come lo calcola il compilatore? – root

+0

Questa è una buona risposta. Implicita in questa risposta: l'espressione "10 + 5-3" è temporaneamente memorizzata nella memoria di lavoro del compilatore, probabilmente come una struttura di dati "albero di espressioni". Quindi le ottimizzazioni del compilatore (in questo caso, la propagazione costante) "risolveranno" l'albero dell'espressione in cui ha valori costanti. Il compilatore risulterà con un "12" e 12 sarà l'operando di una delle istruzioni in linguaggio assembly del programma. –

+0

Un buon libro sugli ottimizzatori di compilation è Muchnik, Advanced Compiler Design & Implementation. –

5

Le costanti sono probabilmente semplificate in fase di compilazione, quindi la tua domanda come posta letteralmente potrebbe non essere d'aiuto. Ma qualcosa come, ad esempio, i - j + k che deve essere calcolato in runtime da alcune variabili, può essere "memorizzato" ovunque piaccia al compilatore, a seconda dell'architettura della CPU: il compilatore cercherà tipicamente di fare del suo meglio per usare i registri, ad es.

LOAD AX, i 
SUB AX, j 
ADD AX, k 

di calcolare una tale espressione "memorizzare" nel registro accumulatore AX, prima di assegnare a qualche posizione di memoria con STORE AX, dest o simili.Sarei piuttosto sorpreso se un moderno compilatore di ottimizzazione su un'architettura CPU semi-decente (si, x86 incluso!) Fosse necessario per riversare i registri in memoria per qualsiasi espressione ragionevolmente semplice!

+0

Ho eliminato la mia risposta, quindi potresti voler eliminare "dice GMan". :) – GManNickG

0
  • Il risultato del calcolo nell'RHS (lato destro) viene calcolato dal compilatore in un passaggio denominato "propagazione costante".
  • Poi, viene memorizzato come operando dell'istruzione gruppo mobile il valore in a

Ecco uno smontaggio da MSVC:

int a; 
    a = 10 + 5 - 3; 

0041338E mov   dword ptr [a],0Ch 
1

Dove lo memorizza in realtà totalmente fino a il compilatore. Lo standard non impone questo comportamento.

Un tipico posto può essere visto dal fatto compilare il codice e guardando l'output assembler:

int main (int argc, char *argv[]) { 
    int a; 
    a = 10 + 5 - 3; 
    return 0; 
} 

che produce:

 .file "qq.c" 
     .def ___main; 
      .scl 2; 
      .type 32; 
     .endef 
     .text 
.globl _main 
     .def _main; 
      .scl 2; 
      .type 32; 
     .endef 
_main: 
     pushl %ebp 
     movl %esp, %ebp 
     subl $8, %esp 
     andl $-16, %esp 
     movl $0, %eax 
     addl $15, %eax 
     addl $15, %eax 
     shrl $4, %eax 
     sall $4, %eax 
     movl %eax, -8(%ebp) 
     movl -8(%ebp), %eax 
     call __alloca 
     call ___main 
     movl $12, -4(%ebp)   ;***** 
     movl $0, %eax 
     leave 
     ret 

Il bit corrispondente è segnata ;***** e si può vedere che il valore è stato creato dal compilatore e inserito direttamente in un'istruzione di tipo mov.

Si noti che è solo così semplice perché l'espressione è un valore costante. Non appena si introducono valori non costanti (come le variabili), il codice diventa un po 'più complicato. Questo perché devi vedere quelle variabili in memoria (o potrebbero già essere in un registro) e quindi manipolare i valori al run-time, non in fase di compilazione.

Quanto al modo in cui il compilatore calcola quale sia il valore dovrebbe essere, questo è a che fare con la valutazione di espressione ed è tutta un'altra domanda :-)

+0

Buon esempio. Stavo per dire "guarda l'assemblaggio generato" ma ero troppo pigro per creare un esempio. :) Nota anche, può diventare più complicato se il valore costante è più grande. Alcuni processori hanno un limite (ad esempio 8 bit o 16 bit) su quanto grande può essere inserito direttamente un valore in un'istruzione. –

0

La tua domanda è basata su una premessa errata.

La proprietà che definisce Ivalue in C è che ha un posto in deposito, cioè viene memorizzato . Questo è ciò che differenzia lvalue da rvalore. Rvalue è non memorizzato ovunque. Questo è ciò che lo rende un valore. Se fosse memorizzato, sarebbe lvalue per definizione.

+0

Deve essere memorizzato * da qualche parte * altrimenti non è possibile utilizzarlo :-) – paxdiablo

+1

No, non è così. Ad esempio, quando si inizializza una variabile con '0', il codice macchina potrebbe semplicemente' xor' un registro con se stesso. '0'" memorizzato da qualche parte "in questo caso? No. Un '1' può essere generato come un incremento del registro precedentemente azzerato. E così via. I valori non devono essere memorizzati ovunque. Questo è ciò che li rende rvalues. – AnT

0

I termini "lvalue" e "rvalue" vengono utilizzati per bisecare il mondo delle espressioni. Cioè, (10+5-3) è un'espressione che sembra essere un valore rvalue (perché non è possibile applicare l'operatore & ad esso - in C++ le regole sono più complicate). Al momento dell'esecuzione, non ci sono espressioni, lvalue o valori. In particolare, non vengono memorizzati da nessuna parte.

vi stavate chiedendo dove il valore 12 è stato memorizzato, ma il valore di 12 non è né un lvalue né un rvalue (in contrasto con l'espressione 12 che sarebbe un rvalue, ma 12 non compare nel vostro programma).

Problemi correlati