Come funziona lo storage const? (Articolo 2, Scott Myers Effective C ++)

8

Nell'articolo 2 a pagina 16, (Preferisci consensi, enumerazioni e inline per #definire), Scott dice:

Also, though good compilers won't set aside storage for const objects of integer types...

Non lo capisco. Se definisco un oggetto const, ad es.

const int myval = 5;

allora sicuramente il compilatore deve mettere da parte della memoria (di dimensione int) per memorizzare il valore 5?

O i dati const sono memorizzati in qualche modo speciale?

Questa è più una questione di archiviazione su computer, suppongo. In sostanza, in che modo il computer memorizza gli oggetti const in modo che nessuno spazio di archiviazione sia messo da parte?

    
posta user619818 01.04.2012 - 21:57
fonte

6 risposte

7

Also, though good compilers won't set aside storage for const objects of integer types...

Un'istruzione leggermente più corretta sarebbe che i compilatori non metterebbero da parte memoria dati per gli oggetti const di tipo intero: lo scambieranno per memoria di programma . Non c'è differenza tra i due sotto l'architettura von Neumann, ma in altre architetture, come Harvard, la distinzione è piuttosto importante.

Per comprendere appieno cosa sta succedendo, è necessario ricordare come il linguaggio assembly carica i dati per l'elaborazione. Esistono due modi fondamentali per caricare i dati: leggere una memoria da una posizione specifica (la cosiddetta modalità di indirizzamento diretta ), o impostare una costante specificata come parte dell'istruzione stessa (il cosiddetto immediato modalità di indirizzamento). Quando il compilatore vede una dichiarazione const int x = 5 seguita da int a = x+x , ha due opzioni:

  • Metti 5 in una memoria dati, come se fosse una variabile, e generi istruzioni per il caricamento diretto; considera le scritture su x come errori
  • Ogni volta che si fa riferimento a x , genera un'istruzione di carico immediato del valore 5

Nel primo caso vedrai una lettura da x nel registro accumulator , un'aggiunta del valore nella posizione di x all'accumulatore e un negozio nella posizione di %codice%. Nel secondo caso vedrai un carico immediato di cinque, un add immediato di cinque, seguito da un negozio nella posizione di a . Alcuni compilatori potrebbero capire che stai aggiungendo una costante a se stessa, ottimizza a in a = x+x e genera una singola istruzione che ne memorizza dieci nella posizione di a = 10 .

    
risposta data 02.04.2012 - 01:39
fonte
10

Non necessariamente. Può anche decidere di utilizzare solo il valore grezzo 5 invece di myval nel codice compilato.

La differenza tra #define MYVAL 5 e const int myval = 5 è che nel primo caso il compilatore non ha alcuna scelta, dal momento che il preprocessore ha già sostituito tutte le menzioni di MYVAL nel codice sorgente con 5 nel momento in cui il compilatore il compilatore arriva a vedere il codice sorgente. In quest'ultimo caso però, c'è una scelta. Per una build di debug non ottimizzata, il compilatore può allocare esplicitamente un const int , quindi nel debugger potrai vedere la costante myval , invece del solo valore grezzo 5.

    
risposta data 01.04.2012 - 22:03
fonte
3

Ruberò la prima frase dalla risposta di Péter Török ma elaborerò in modo diverso: Non necessariamente. Può anche decidere di utilizzare solo il valore grezzo 5 invece di myval nel codice compilato.

Trattare myval come una variabile regolare allocando lo spazio per essa in memoria può avere implicazioni sulle prestazioni che vanno da minuscolo a grave a seconda dell'architettura e di come gestisce la memoria.

Lavorando in questo modo, un compilatore emetterebbe un'istruzione che dice qualcosa sulla falsariga di "load register R con qualunque cosa si trovi nella posizione di memoria per myval ". La posizione di myval come un operando dell'istruzione, in modo tale che essa esca dallo stesso blocco di dati dell'istruzione stessa. Sulle moderne CPU, questo valore sarà prontamente disponibile on-chip a causa del prefetch delle istruzioni. Con l'indirizzo in mano, la CPU deve ancora estrarre il valore dalla memoria. Potrebbe andare rapidamente se la posizione è nelle vicinanze nella cache o no così rapidamente se non lo è. Non solo la CPU deve andare fuori dal chip per ottenere il valore, così facendo potrebbe costringerlo a scaricare altri dati più utili dalla cache che dovranno essere riportati in seguito. Quando il programma è in esecuzione su un sistema operativo che virtualizza la memoria, l'accesso a tale posizione potrebbe causare un errore di pagina, causando la sospensione del programma fino a quando la pagina richiesta viene portata nella RAM tramite I / O periferici (ad esempio, disco), il programma riavviato tramite un interruttore di contesto e il meccanismo della cache fa tutto ciò che ha intenzione di fare con esso.

Collegando a fondo il valore costante nel codice oggetto, il compilatore emetterebbe un'istruzione come "load register R con il valore 5 ." Come l'indirizzo di memoria descritto sopra, il 5 sarebbe un operando dell'istruzione e disponibile allo stesso modo (vale a dire, precaricato). Ecco dove finisce la somiglianza, perché ora la CPU ha tutto il necessario per mettere la 5 nel registro R e andare avanti con la sua attività. Poiché gli indirizzi e i registri di solito hanno le stesse dimensioni, non c'è differenza nel numero di byte occupati dall'istruzione e l'effettiva esecuzione avviene con zero possibilità di errori di cache e di errori di pagina che possono verificarsi quando si pesca qualcosa fuori memoria.

Il compilatore potrebbe, come ha sottolineato Péter, allocare spazio e un simbolo per myval nei build di debug. Non ci sarebbe nulla di male nel fare ciò e continueremo a collegare il suo valore, dal momento che il valore rimane lo stesso indipendentemente dal fatto che il simbolo sia davvero lì solo per noi umani da usare nel debugging.

Si noti che questo si applica solo ai valori che possono essere memorizzati nei registri, poiché i registri sono interi per natura. Altre costanti finiranno nella memoria.

    
risposta data 01.04.2012 - 23:37
fonte
2

Quello che la citazione dice non è del tutto corretto.

Un buon compilatore non metterà da parte la memoria per le variabili const statiche . Se la variabile const non è statica e si trova in ambito file, deve mettere da parte la memoria perché la variabile può essere referenziata da un'altra unità di compilazione. Con le ottimizzazioni link-time, il linker potrebbe essere in grado di eliminare le istruzioni di archiviazione e riscrittura che fanno riferimento alla variabile se può dimostrare che il programma non genera un puntatore a quella variabile.

Un motivo molto migliore per usare const int invece di #define è che i debugger non "vedono" le macro, quindi non puoi controllare un valore di #defined nel debugger.

    
risposta data 02.04.2012 - 12:35
fonte
1

Il compilatore sostituirà il numero cinque ovunque venga utilizzata la variabile 'myval'.

    
risposta data 01.04.2012 - 22:03
fonte
0

Il compilatore può considerare i valori const come operandi immediati. Gli operandi immediati non richiedono l'archiviazione dei dati. Il compilatore può trattare:

int foo = myVal;

lo stesso di

int foo = 5;

Il valore 5 non è memorizzato nella memoria dati, ma piuttosto come parte della sequenza di istruzioni.

Il compilatore deve riservare l'archiviazione dei dati nei casi in cui l'indirizzo di un valore può essere preso. Anche in questo caso, il compilatore utilizzerà comunque operazioni immediate quando viene utilizzato il valore di myVal.

    
risposta data 02.04.2012 - 17:40
fonte

Leggi altre domande sui tag