@GMan: ANSI C não permite obter o endereço de um objeto de registro; esta restrição não se aplica a C ++
Brian R. Bondy
1
@Brian: Hm, você está certo. Está apenas em uma nota agora (que provavelmente será ignorado se o endereço for usado), mas não obrigatório. Bom saber. (Bem, mais ou menos.: P)
GManNickG
8
A votação para reabrir registertem semânticas diferentes entre C e C ++.
CB Bailey
3
como conseqüência disso, em C é possível proibir a conversão de array em ponteiro fazendo um registro de array: register int a[1];com essa declaração, você não pode indexar esse array. Se você tentar, você faz UB
Johannes Schaub - litb 08/07/10
2
Na verdade, votei para reabrir. Votei pelo fechamento antes de saber que havia uma diferença.
GManNickG
Respostas:
24
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;.
Essa resposta não é mais correta, desde 2011, a palavra-chave autonão pode ser simplesmente omitida ... Talvez você possa atualizar sua resposta.
Walter
2
@Walter: Você pode citar o que mudou? Não acompanhei todas as mudanças de idioma.
supercat de
2
@supercat, sim, por enquanto, mas registerestá obsoleto e haverá uma proposta para removê-lo do C ++ 17.
Jonathan Wakely
3
De acordo com en.cppreference.com/w/cpp/language/auto , post C ++ 11, 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"):
Guillaume
96
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).
Desde C ++ 17, ele está obsoleto, não usado e reservado, no entanto.
ZachB
@ZachB, isso está incorreto; O registro é reservado no C ++ 17, mas ainda funciona e funciona quase de forma idêntica ao registro do C.
Lewis Kelsey de
@LewisKelsey Não é usado e está reservado na especificação C ++ 17; não é um dos 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.
Com os compiladores de hoje, provavelmente nada. Originalmente, era uma dica para colocar uma variável em um registrador para acesso mais rápido, mas a maioria dos compiladores hoje ignora essa dica e decide por si.
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.
E eu acrescentaria que a palavra-chave 'registrar' só seria útil em um microcontrolador executando um único programa C ++ sem threads e sem multitarefa. O programa C ++ teria que possuir toda a CPU para garantir que a variável de 'registro' não fosse movida dos registros especiais da CPU.
Santiago Villafuerte
@SantiagoVillafuerte deseja adicioná-lo editando a resposta?
ncomputers
Não tenho certeza da minha resposta ... embora pareça plausível. Prefiro deixar como um comentário para que outras pessoas o aprovem ou desaprovem.
Santiago Villafuerte
1
@SantiagoVillafuerte Isso não é verdade, em sistemas multitarefa quando a mudança de contexto do SO - não da aplicação - é responsável por salvar / restaurar registros. Já que você não está trocando de contexto após cada instrução da CPU, colocar coisas em registradores é absolutamente significativo. As outras respostas aqui (que os compiladores simplesmente não se importam com sua opinião quando se trata de alocação de registros) são mais precisas.
Cubic de
O exemplo que você mostrou está, na verdade, usando a extensão Explicit Register Variables do GCC , que é diferente do registerespecificador de classe de armazenamento e ainda é suportado pelo GCC.
ZachB de
2
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++intmain(void){
constregisterint i = 3;
printf("%d", i);
return0;
}
.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 printfxor 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.
register
tem 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 UBRespostas:
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
auto
palavra-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 aint i=5;
quando usada dentro de um contexto de bloco eauto 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 enquantoauto i=(unsigned char)5;
se tornou equivalente aauto unsigned char i=5;
.fonte
auto
não pode ser simplesmente omitida ... Talvez você possa atualizar sua resposta.register
está obsoleto e haverá uma proposta para removê-lo do C ++ 17.auto
agora é 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-chaveregister
(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)" :
fonte
storage-class-specifier
da 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 ++.fonte
Com os compiladores de hoje, provavelmente nada. Originalmente, era uma dica para colocar uma variável em um registrador para acesso mais rápido, mas a maioria dos compiladores hoje ignora essa dica e decide por si.
fonte
Quase certamente nada.
register
é uma dica para o compilador que você planeja usarx
muito 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.
fonte
register
está obsoleto no C ++ 11. Não é usado e está reservado em C ++ 17.Fonte: http://en.cppreference.com/w/cpp/keyword/register
fonte
A
register
palavra-chave foi útil para:Um exemplo de sistema produtivo, onde a
register
palavra - 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 .
fonte
register
especificador 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 Cregister
ao compilar sem sinalizadores de otimização -O1 –- Ofast em relação a esta resposta. Usar clang ++ - 7 causa um erro do compilador. Portanto, sim,register
otimizaçõ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
volatile
e seus endereços não podem ser pegos), caso contrário, ele se comportará como você não usouregister
e o valor será armazenado na pilha.Em -O0, outra diferença é que
const register
em gcc C e gcc C ++ não se comportam da mesma forma. No gcc C,const register
se comporta comoregister
, porque os escopos de blococonst
não são otimizados no gcc. No clang C,register
não faz nada e somenteconst
as otimizações de escopo de bloco se aplicam. No gcc C, asregister
otimizações se aplicam, masconst
no escopo do bloco não há otimização. No gcc C ++, tantoregister
econst
otimizaçõ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
register
diz ao compilador para 1) armazenar uma variável local em um registro salvo do callee, neste casorbx
, e 2) otimizar as gravações da pilha se o endereço da variável nunca for obtido .const
diz 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 ++,
register
por si só parece criar um intervalo aleatório de 16 bytes na pilha para o primeiro local na pilha, o que não acontece comconst register
.Compilando usando -Ofast no entanto;
register
tem 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á;const
ainda otimiza a carga em C e C ++, mas apenas no escopo do arquivo ;volatile
ainda 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
fonte
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
register
palavra - 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
register
dicas 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ê-lasregister
. O ideal é que o seu criador de perfil se comunique de alguma forma com o compilador e evite que você pense nessas nuances.fonte