Qual è la differenza tra
int x=7;
e
register int x=7;
?
Sto usando C ++.
Qual è la differenza tra
int x=7;
e
register int x=7;
?
Sto usando C ++.
registerha una semantica diversa tra C e C ++.
register int a[1];con quella dichiarazione, non è possibile indicizzare quell'array. Se ci provi, fai UB
Risposte:
In C ++ come esisteva nel 2010, qualsiasi programma valido che utilizza le parole chiave "auto" o "register" sarà semanticamente identico a uno con quelle parole chiave rimosse (a meno che non compaiano in macro in stringa o altri contesti simili). In questo senso le parole chiave sono inutili per la corretta compilazione dei programmi. D'altra parte, le parole chiave potrebbero essere utili in determinati contesti macro per garantire che l'uso improprio di una macro causi un errore in fase di compilazione piuttosto che produrre codice fasullo.
In C ++ 11 e nelle versioni successive del linguaggio, la autoparola chiave è stata riproposta per agire come uno pseudo-tipo per gli oggetti inizializzati, che un compilatore sostituirà automaticamente con il tipo di espressione di inizializzazione. Pertanto, in C ++ 03, la dichiarazione: auto int i=(unsigned char)5;era equivalente a int i=5;quando utilizzata all'interno di un contesto di blocco ed auto i=(unsigned char)5;era una violazione dei vincoli. In C ++ 11, è auto int i=(unsigned char)5;diventato una violazione dei vincoli mentre è auto i=(unsigned char)5;diventato equivalente a auto unsigned char i=5;.
autonon può essere semplicemente omessa ... Forse potresti aggiornare la tua risposta.
registerè deprecato e ci sarà una proposta per rimuoverlo per C ++ 17.
autoora viene utilizzato per la deduzione automatica del tipo Ma prima era utilizzato per specificare che si desiderava che la variabile fosse memorizzata "automaticamente" ( quindi sullo stack immagino) in contrasto con la parola chiave register(che significa "registro del processore"):
register è un suggerimento per il compilatore, consigliandogli di memorizzare quella variabile in un registro del processore invece che in memoria (ad esempio, invece che nello stack).
Il compilatore può o non può seguire quel suggerimento.
Secondo Herb Sutter in "Parole chiave che non sono (o commenti con un altro nome)" :
Uno specificatore di registro ha la stessa semantica di uno specificatore automatico ...
storage-class-specifiernella grammatica e non ha una semantica definita. Un compilatore conforme può generare un errore come fa Clang. Tuttavia alcune implementazioni lo consentono ancora e lo ignorano (MSVC, ICC) o lo usano come suggerimento per l'ottimizzazione (GCC). Vedi open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . Tuttavia, ho sbagliato a parlare su un punto: era deprecato in C ++ 11.
Secondo Herb Sutter , registerè " esattamente significativo quanto gli spazi bianchi " e non ha alcun effetto sulla semantica di un programma C ++.
Quasi certamente niente.
registerè un suggerimento per il compilatore che prevedi di usare xmolto e che pensi che dovrebbe essere inserito in un registro.
Tuttavia, i compilatori ora sono molto più bravi nel determinare quali valori dovrebbero essere inseriti nei registri rispetto al programmatore medio (o anche esperto), quindi i compilatori ignorano la parola chiave e fanno quello che vogliono.
registerè deprecato in C ++ 11. È inutilizzato e riservato in C ++ 17.
La registerparola chiave è stata utile per:
Un esempio di sistema produttivo, dove registerera richiesta la parola chiave:
typedef unsigned long long Out;
volatile Out out,tmp;
Out register rax asm("rax");
asm volatile("rdtsc":"=A"(rax));
out=out*tmp+rax;
È stato deprecato da C ++ 11 ed è inutilizzato e riservato in C ++ 17 .
registerspecificatore della classe di archiviazione ed è ancora supportata da GCC.
Si consideri un caso in cui l'ottimizzatore del compilatore ha due variabili ed è costretto a riversarne una nello stack. È successo così che entrambe le variabili hanno lo stesso peso per il compilatore. Dato che non c'è differenza, il compilatore riverserà arbitrariamente una delle variabili. D'altra parte, la registerparola chiave fornisce al compilatore un suggerimento su quale variabile sarà accessibile più frequentemente. È simile all'istruzione di prefetch x86, ma per l'ottimizzatore del compilatore.
Ovviamente i registersuggerimenti sono simili ai suggerimenti di probabilità di branch forniti dall'utente e possono essere dedotti da questi suggerimenti di probabilità. Se il compilatore sa che qualche ramo viene preso spesso, manterrà le variabili relative al ramo nei registri. Quindi suggerisco di preoccuparti di più dei suggerimenti sui rami e di dimenticarli register. Idealmente il tuo profiler dovrebbe comunicare in qualche modo con il compilatore e risparmiarti persino di pensare a tali sfumature.
A partire da gcc 9.3, la compilazione usando -std=c++2a, register produce un avviso del compilatore, ma ha ancora l'effetto desiderato e si comporta in modo identico al C registerquando si compila senza -O1 –- Flag di ottimizzazione veloce rispetto a questa risposta. L'uso di clang ++ - 7 causa tuttavia un errore del compilatore. Quindi sì,register ottimizzazioni fanno la differenza solo nella compilazione standard senza flag di ottimizzazione -O, ma sono ottimizzazioni di base che il compilatore potrebbe capire anche con -O1.
L'unica differenza è che in C ++, puoi prendere l'indirizzo della variabile di registro, il che significa che l'ottimizzazione avviene solo se non prendi l'indirizzo della variabile o dei suoi alias (per creare un puntatore) o prendi un riferimento di esso nel codice (solo su - O0, perché un riferimento ha anche un indirizzo, perché è un puntatore const nello stack , che, come un puntatore può essere ottimizzato fuori dallo stack se si compila usando -Ofast, tranne che non appariranno mai sullo stack usando -Ofast, perché a differenza di un puntatore, non possono essere creati volatilee i loro indirizzi non possono essere presi), altrimenti si comporterà come se non avessi usatoregister e il valore verrà memorizzato nello stack.
Su -O0, un'altra differenza è che const registersu gcc C e gcc C ++ non si comportano allo stesso modo. Su gcc C, const registersi comporta come register, perché i block-scope constnon sono ottimizzati su gcc. Su clang C, registernon fa nulla e si constapplicano solo le ottimizzazioni dell'ambito del blocco. Su gcc C, le registerottimizzazioni si applicano ma constnell'ambito del blocco non ha ottimizzazione. Su gcc C ++, sia registere constottimizzazioni block-scope combinano.
#include <stdio.h> //yes it's C code on C++
int main(void) {
const register int i = 3;
printf("%d", i);
return 0;
}
int i = 3;:
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 3
mov eax, DWORD PTR [rbp-4]
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
leave
ret
register int i = 3;:
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
push rbx
sub rsp, 8
mov ebx, 3
mov esi, ebx
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
mov rbx, QWORD PTR [rbp-8] //callee restoration
leave
ret
const int i = 3;
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 3 //still saves to stack
mov esi, 3 //immediate substitution
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
leave
ret
const register int i = 3;
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
mov esi, 3 //loads straight into esi saving rbx push/pop and extra indirection (because C++ block-scope const is always substituted immediately into the instruction)
mov edi, OFFSET FLAT:.LC0 // can't optimise away because printf only takes const char*
mov eax, 0 //zeroed: https://stackoverflow.com/a/6212755/7194773
call printf
mov eax, 0 //default return value of main is 0
pop rbp //nothing else pushed to stack -- more efficient than leave (rsp == rbp already)
ret
registerdice al compilatore di 1) memorizzare una variabile locale in un registro salvato chiamato, in questo caso rbx, e 2) ottimizzare le scritture dello stack se l'indirizzo della variabile non viene mai preso . constdice al compilatore di sostituire immediatamente il valore (invece di assegnargli un registro o caricarlo dalla memoria) e di scrivere la variabile locale nello stack come comportamento predefinito. const registerè la combinazione di queste ottimizzazioni incoraggiate. Questo è il più sottile possibile.
Inoltre, su gcc C e C ++, register da solo sembra creare un gap casuale di 16 byte nello stack per il primo locale nello stack, cosa che non accade con const register.
Compilare usando -Ofast comunque; registerha un effetto di ottimizzazione 0 perché se può essere inserito in un registro o reso immediato, lo sarà sempre e se non può non lo sarà; constottimizza ancora il carico su C e C ++ ma solo nell'ambito del file ; volatileforza ancora i valori da memorizzare e caricare dallo stack.
.LC0:
.string "%d"
main:
//optimises out push and change of rbp
sub rsp, 8 //https://stackoverflow.com/a/40344912/7194773
mov esi, 3
mov edi, OFFSET FLAT:.LC0
xor eax, eax //xor 2 bytes vs 5 for mov eax, 0
call printf
xor eax, eax
add rsp, 8
ret