Qual é a diferença entre
int x=7;
e
register int x=7;
?
Estou usando C ++.
Qual é a diferença entre
int x=7;
e
register int x=7;
?
Estou usando C ++.
registertem semânticas diferentes entre C e C ++.
register int a[1];com essa declaração, você não pode indexar esse array. Se você tentar, você faz UB
Respostas:
Em C ++ como existia em 2010, qualquer programa válido que use as palavras-chave "auto" ou "registrar" será semanticamente idêntico a um com as palavras-chave removidas (a menos que apareçam em macros string ou outros contextos semelhantes). Nesse sentido, as palavras-chave são inúteis para programas de compilação adequada. Por outro lado, as palavras-chave podem ser úteis em certos contextos de macro para garantir que o uso impróprio de uma macro causará um erro em tempo de compilação em vez de produzir código falso.
Em C ++ 11 e versões posteriores da linguagem, a autopalavra-chave foi redefinida para atuar como um pseudo-tipo para objetos que são inicializados, os quais um compilador irá substituir automaticamente pelo tipo da expressão de inicialização. Assim, em C ++ 03, a declaração: auto int i=(unsigned char)5;era equivalente a int i=5;quando usada dentro de um contexto de bloco e auto i=(unsigned char)5;era uma violação de restrição. No C ++ 11, auto int i=(unsigned char)5;tornou-se uma violação de restrição enquanto auto i=(unsigned char)5;se tornou equivalente a auto unsigned char i=5;.
autonão pode ser simplesmente omitida ... Talvez você possa atualizar sua resposta.
registerestá obsoleto e haverá uma proposta para removê-lo do C ++ 17.
autoagora é usado para dedução automática de tipo. Mas antes disso, era usado para especificar que você queria que sua variável fosse armazenada "automaticamente" ( portanto, na pilha, eu acho) em oposição à palavra-chave register(que significa "registro do processador"):
register é uma dica para o compilador, aconselhando-o a armazenar essa variável em um registro do processador em vez de na memória (por exemplo, em vez da pilha).
O compilador pode ou não seguir essa dica.
De acordo com Herb Sutter em "Palavras-chave que não são (ou comentários com outro nome)" :
Um especificador de registro tem a mesma semântica de um especificador automático ...
storage-class-specifierda gramática e não tem semântica definida. Um compilador em conformidade pode gerar um erro como o Clang faz. No entanto, algumas implementações ainda permitem e ignoram (MSVC, ICC) ou usam como uma dica de otimização (GCC). Consulte open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . Eu falei errado em um ponto: ele foi descontinuado no C ++ 11.
De acordo com Herb Sutter , registeré " exatamente tão significativo quanto o espaço em branco " e não tem efeito na semântica de um programa C ++.
Quase certamente nada.
registeré uma dica para o compilador que você planeja usar xmuito e que você acha que deveria ser colocado em um registrador.
No entanto, os compiladores agora são muito melhores em determinar quais valores devem ser colocados nos registradores do que o programador médio (ou mesmo especialista), então os compiladores simplesmente ignoram a palavra-chave e fazem o que desejam.
registerestá obsoleto no C ++ 11. Não é usado e está reservado em C ++ 17.
A registerpalavra-chave foi útil para:
Um exemplo de sistema produtivo, onde a registerpalavra - chave foi necessária:
typedef unsigned long long Out;
volatile Out out,tmp;
Out register rax asm("rax");
asm volatile("rdtsc":"=A"(rax));
out=out*tmp+rax;
Ele está obsoleto desde C ++ 11 e não é usado e é reservado no C ++ 17 .
registerespecificador de classe de armazenamento e ainda é suportado pelo GCC.
A partir do gcc 9.3, compilar usando -std=c++2a, register produz um aviso do compilador, mas ainda tem o efeito desejado e se comporta de forma idêntica ao C registerao compilar sem sinalizadores de otimização -O1 –- Ofast em relação a esta resposta. Usar clang ++ - 7 causa um erro do compilador. Portanto, sim, registerotimizações só fazem diferença na compilação padrão sem sinalizadores -O de otimização, mas são otimizações básicas que o compilador descobriria mesmo com -O1.
A única diferença é que em C ++, você pode pegar o endereço da variável de registro, o que significa que a otimização só ocorre se você não pegar o endereço da variável ou seus apelidos (para criar um ponteiro) ou tomar uma referência dele no código (somente em - O0, porque uma referência também tem um endereço, porque é um ponteiro const na pilha , que, como um ponteiro pode ser otimizado fora da pilha se for compilado usando -Ofast, exceto que eles nunca aparecerão na pilha usando -Ofast, porque ao contrário de um ponteiro, eles não podem ser feitos volatilee seus endereços não podem ser pegos), caso contrário, ele se comportará como você não usou registere o valor será armazenado na pilha.
Em -O0, outra diferença é que const registerem gcc C e gcc C ++ não se comportam da mesma forma. No gcc C, const registerse comporta como register, porque os escopos de bloco constnão são otimizados no gcc. No clang C, registernão faz nada e somente constas otimizações de escopo de bloco se aplicam. No gcc C, as registerotimizações se aplicam, mas constno escopo do bloco não há otimização. No gcc C ++, tanto registere constotimizações bloco de escopo de combinar.
#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
registerdiz ao compilador para 1) armazenar uma variável local em um registro salvo do callee, neste caso rbx, e 2) otimizar as gravações da pilha se o endereço da variável nunca for obtido . constdiz ao compilador para substituir o valor imediatamente (em vez de atribuir a ele um registro ou carregá-lo da memória) e gravar a variável local na pilha como comportamento padrão. const registeré a combinação dessas otimizações encorajadas. Isso é o mais fino possível.
Além disso, no gcc C e C ++, registerpor si só parece criar um intervalo aleatório de 16 bytes na pilha para o primeiro local na pilha, o que não acontece com const register.
Compilando usando -Ofast no entanto; registertem 0 efeito de otimização porque se puder ser colocado em um cadastro ou tornado imediato, sempre será e se não puder não será; constainda otimiza a carga em C e C ++, mas apenas no escopo do arquivo ; volatileainda força os valores a serem armazenados e carregados da pilha.
.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
Considere um caso em que o otimizador do compilador tem duas variáveis e é forçado a espalhar uma na pilha. Acontece que ambas as variáveis têm o mesmo peso para o compilador. Dado que não há diferença, o compilador irá espalhar arbitrariamente uma das variáveis. Por outro lado, a registerpalavra - chave dá ao compilador uma dica de qual variável será acessada com mais frequência. É semelhante à instrução de pré-busca x86, mas para otimizador de compilador.
Obviamente, as registerdicas são semelhantes às dicas de probabilidade de ramos fornecidas pelo usuário e podem ser inferidas a partir dessas dicas de probabilidade. Se o compilador souber que algum branch é usado com freqüência, ele manterá as variáveis relacionadas ao branch nos registradores. Portanto, sugiro que se preocupe mais com as dicas de branch e esquecê-las register. O ideal é que o seu criador de perfil se comunique de alguma forma com o compilador e evite que você pense nessas nuances.