@GMan: ANSI C no permite tomar la dirección de un objeto de registro; esta restricción no se aplica a C ++
Brian R. Bondy
1
@Brian: Hm, tienes razón. Ahora está solo en una nota (que probablemente se ignorará si se toma la dirección), pero no es obligatorio. Bueno saber. (Bueno, más o menos.: P)
GManNickG
8
Votar para reabrir registertiene una semántica diferente entre C y C ++.
CB Bailey
3
como consecuencia de esto, en C es posible prohibir la conversión de matriz a puntero haciendo un registro de matriz: register int a[1];con esa declaración, no puede indexar esa matriz. Si lo intenta, lo hace UB
Johannes Schaub - litb
2
De hecho, voté a favor de reabrir. Voté para cerrar antes de saber que había una diferencia.
GManNickG
Respuestas:
24
En C ++ tal como existía en 2010, cualquier programa válido que utilice las palabras clave "auto" o "registro" será semánticamente idéntico a uno con esas palabras clave eliminadas (a menos que aparezcan en macros en cadena u otros contextos similares). En ese sentido, las palabras clave son inútiles para compilar programas correctamente. Por otro lado, las palabras clave pueden ser útiles en ciertos contextos de macros para garantizar que el uso inadecuado de una macro provoque un error en tiempo de compilación en lugar de producir código falso.
En C ++ 11 y versiones posteriores del lenguaje, la autopalabra clave se rediseñó para actuar como un pseudo-tipo para los objetos que se inicializan, que un compilador reemplazará automáticamente con el tipo de la expresión de inicialización. Por lo tanto, en C ++ 03, la declaración: auto int i=(unsigned char)5;era equivalente a int i=5;cuando se usaba dentro de un contexto de bloque y auto i=(unsigned char)5;era una violación de restricción. En C ++ 11, se auto int i=(unsigned char)5;convirtió en una violación de restricción mientras se auto i=(unsigned char)5;convirtió en equivalente a auto unsigned char i=5;.
Esta respuesta ya no es correcta, desde 2011, la palabra clave autono se puede simplemente omitir ... Quizás podría actualizar su respuesta.
Walter
2
@Walter: ¿Puedes citar qué ha cambiado? No he seguido todos los cambios de idioma.
supercat
2
@supercat, sí, por el momento, pero registerestá desaprobado y habrá una propuesta para eliminarlo para C ++ 17.
Jonathan Wakely
3
De acuerdo con en.cppreference.com/w/cpp/language/auto , la publicación C ++ 11 autoahora se usa para la deducción automática de tipos Pero antes, se usaba para especificar que deseaba que su variable se almacenara "automáticamente" ( por lo tanto, en la pila , supongo) en lugar de la palabra clave register(que significa "registro del procesador"):
Guillaume
97
register es una pista para el compilador, aconsejándole que almacene esa variable en un registro del procesador en lugar de la memoria (por ejemplo, en lugar de la pila).
Sin embargo, desde C ++ 17, está obsoleto, sin uso y reservado.
ZachB
@ZachB, esto es incorrecto; El registro está reservado en C ++ 17 pero todavía funciona y funciona casi de manera idéntica al registro de C.
Lewis Kelsey
@LewisKelsey No se utiliza y está reservado en la especificación C ++ 17; no es uno de los storage-class-specifierde la gramática y no tiene semántica definida. Un compilador conforme puede arrojar un error como lo hace Clang. No obstante, algunas implementaciones aún lo permiten y lo ignoran (MSVC, ICC) o lo usan como una sugerencia de optimización (GCC). Consulte open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . Sin embargo, me equivoqué en un punto: estaba obsoleto en C ++ 11.
Con los compiladores de hoy, probablemente nada. Originalmente, era una sugerencia para colocar una variable en un registro para un acceso más rápido, pero la mayoría de los compiladores ignoran esa sugerencia y deciden por sí mismos.
registeres una pista para el compilador de que planea usar xmucho y que cree que debería colocarse en un registro.
Sin embargo, los compiladores ahora son mucho mejores para determinar qué valores deben colocarse en los registros que el programador promedio (o incluso experto), por lo que los compiladores simplemente ignoran la palabra clave y hacen lo que quieren.
Y agregaría que la palabra clave 'registrar' solo sería útil en un microcontrolador que ejecute un solo programa C ++ sin subprocesos y sin multitarea. El programa C ++ tendría que ser propietario de toda la CPU para asegurarse de que la variable 'registro' no se mueva de los registros especiales de la CPU.
Santiago Villafuerte
@SantiagoVillafuerte ¿quieres agregarlo editando la respuesta?
ncomputers
No estoy tan seguro de mi respuesta ... aunque suena plausible. Prefiero dejarlo como comentario para que otros lo aprueben o desaprueben.
Santiago Villafuerte
1
@SantiagoVillafuerte Esto no es realmente cierto, en los sistemas multitarea cuando el cambio de contexto del sistema operativo, no la aplicación, es responsable de guardar / restaurar los registros. Dado que no está cambiando de contexto después de cada instrucción de la CPU, poner cosas en registros es absolutamente significativo. Las otras respuestas aquí (que a los compiladores simplemente no les importa su opinión cuando se trata de la asignación de registros) son más precisas.
Considere un caso en el que el optimizador del compilador tiene dos variables y se ve obligado a derramar una en la pila. Ocurrió que ambas variables tienen el mismo peso para el compilador. Dado que no hay diferencia, el compilador derramará arbitrariamente una de las variables. Por otro lado, la registerpalabra clave le da al compilador una pista a qué variable se accederá con más frecuencia. Es similar a la instrucción de captación previa x86, pero para el optimizador del compilador.
Obviamente, las registersugerencias son similares a las sugerencias de probabilidad de ramificación proporcionadas por el usuario y se pueden inferir de estas sugerencias de probabilidad. Si el compilador sabe que alguna rama se toma con frecuencia, mantendrá las variables relacionadas con la rama en los registros. Por lo tanto, sugiero que se preocupe más por las sugerencias de rama y se olvide register. Idealmente, su generador de perfiles debería comunicarse de alguna manera con el compilador y evitar que usted piense siquiera en esos matices.
A partir de gcc 9.3, compilación usando -std=c++2a,register produce una advertencia del compilador, pero todavía tiene el efecto deseado y se comporta de forma idéntica a C de registeral compilar sin -O1 - parámetros de optimización Ofast en el respeto de esta respuesta. Sin embargo, el uso de clang ++ - 7 provoca un error del compilador. Así que sí, las registeroptimizaciones solo marcan la diferencia en la compilación estándar sin marcas de optimización -O, pero son optimizaciones básicas que el compilador descubriría incluso con -O1.
La única diferencia es que en C ++, se le permite tomar la dirección de la variable de registro, lo que significa que la optimización solo ocurre si no toma la dirección de la variable o sus alias (para crear un puntero) o toma una referencia de él en el código (solo en - O0, porque una referencia también tiene una dirección, porque es un puntero constante en la pila , que, como un puntero, puede optimizarse fuera de la pila si se compila con -Ofast, excepto que nunca aparecerán en la pila usando -Ofast, porque a diferencia de un puntero, no se pueden hacer volatiley sus direcciones no se pueden tomar), de lo contrario se comportará como si no lo hubiera usado register, y el valor se almacenará en la pila.
En -O0, otra diferencia es que const registeren gcc C y gcc C ++ no se comportan igual. En gcc C, se const registercomporta como register, porque block-scope consts no está optimizado en gcc. En clang C, registerno hace nada y solo se constaplican optimizaciones de alcance de bloque. En gcc C, se registeraplican optimizaciones pero consten el alcance del bloque no tiene optimización. En gcc C ++, se combinan las optimizaciones de alcance de bloque registery de constbloque.
#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
registerle dice al compilador que 1) almacene una variable local en un registro guardado de la persona que llama, en este caso rbx, y 2) optimice las escrituras de la pila si nunca se toma la dirección de la variable . constle dice al compilador que sustituya el valor inmediatamente (en lugar de asignarle un registro o cargarlo desde la memoria) y escribir la variable local en la pila como comportamiento predeterminado. const registeres la combinación de estas optimizaciones envalentonadas. Esto es lo más delgado posible.
Además, en gcc C y C ++, registerpor sí solo parece crear un espacio aleatorio de 16 bytes en la pila para el primer local en la pila, lo que no sucede con const register.
Sin embargo, compilando usando -Ofast; registertiene un efecto de optimización 0 porque si se puede poner en un registro o inmediatamente, siempre lo será y si no, no lo será; consttodavía optimiza la carga en C y C ++ pero solo en el alcance del archivo ; volatiletodavía obliga a que los valores se almacenen y carguen desde la pila.
.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
register
tiene una semántica diferente entre C y C ++.register int a[1];
con esa declaración, no puede indexar esa matriz. Si lo intenta, lo hace UBRespuestas:
En C ++ tal como existía en 2010, cualquier programa válido que utilice las palabras clave "auto" o "registro" será semánticamente idéntico a uno con esas palabras clave eliminadas (a menos que aparezcan en macros en cadena u otros contextos similares). En ese sentido, las palabras clave son inútiles para compilar programas correctamente. Por otro lado, las palabras clave pueden ser útiles en ciertos contextos de macros para garantizar que el uso inadecuado de una macro provoque un error en tiempo de compilación en lugar de producir código falso.
En C ++ 11 y versiones posteriores del lenguaje, la
auto
palabra clave se rediseñó para actuar como un pseudo-tipo para los objetos que se inicializan, que un compilador reemplazará automáticamente con el tipo de la expresión de inicialización. Por lo tanto, en C ++ 03, la declaración:auto int i=(unsigned char)5;
era equivalente aint i=5;
cuando se usaba dentro de un contexto de bloque yauto i=(unsigned char)5;
era una violación de restricción. En C ++ 11, seauto int i=(unsigned char)5;
convirtió en una violación de restricción mientras seauto i=(unsigned char)5;
convirtió en equivalente aauto unsigned char i=5;
.fuente
auto
no se puede simplemente omitir ... Quizás podría actualizar su respuesta.register
está desaprobado y habrá una propuesta para eliminarlo para C ++ 17.auto
ahora se usa para la deducción automática de tipos Pero antes, se usaba para especificar que deseaba que su variable se almacenara "automáticamente" ( por lo tanto, en la pila , supongo) en lugar de la palabra claveregister
(que significa "registro del procesador"):register
es una pista para el compilador, aconsejándole que almacene esa variable en un registro del procesador en lugar de la memoria (por ejemplo, en lugar de la pila).El compilador puede seguir o no esa sugerencia.
Según Herb Sutter en "Palabras clave que no son (o comentarios con otro nombre)" :
fuente
storage-class-specifier
de la gramática y no tiene semántica definida. Un compilador conforme puede arrojar un error como lo hace Clang. No obstante, algunas implementaciones aún lo permiten y lo ignoran (MSVC, ICC) o lo usan como una sugerencia de optimización (GCC). Consulte open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . Sin embargo, me equivoqué en un punto: estaba obsoleto en C ++ 11.Según Herb Sutter ,
register
es " exactamente tan significativo como los espacios en blanco " y no tiene ningún efecto sobre la semántica de un programa C ++.fuente
Con los compiladores de hoy, probablemente nada. Originalmente, era una sugerencia para colocar una variable en un registro para un acceso más rápido, pero la mayoría de los compiladores ignoran esa sugerencia y deciden por sí mismos.
fuente
Casi con certeza nada.
register
es una pista para el compilador de que planea usarx
mucho y que cree que debería colocarse en un registro.Sin embargo, los compiladores ahora son mucho mejores para determinar qué valores deben colocarse en los registros que el programador promedio (o incluso experto), por lo que los compiladores simplemente ignoran la palabra clave y hacen lo que quieren.
fuente
register
está en desuso en C ++ 11. No se utiliza y está reservado en C ++ 17.Fuente: http://en.cppreference.com/w/cpp/keyword/register
fuente
La
register
palabra clave fue útil para:Un ejemplo de un sistema productivo, donde
register
se requería la palabra clave:typedef unsigned long long Out; volatile Out out,tmp; Out register rax asm("rax"); asm volatile("rdtsc":"=A"(rax)); out=out*tmp+rax;
Ha quedado obsoleto desde C ++ 11 y no se utiliza y está reservado en C ++ 17 .
fuente
register
especificador de clase de almacenamiento y aún es compatible con GCC.Considere un caso en el que el optimizador del compilador tiene dos variables y se ve obligado a derramar una en la pila. Ocurrió que ambas variables tienen el mismo peso para el compilador. Dado que no hay diferencia, el compilador derramará arbitrariamente una de las variables. Por otro lado, la
register
palabra clave le da al compilador una pista a qué variable se accederá con más frecuencia. Es similar a la instrucción de captación previa x86, pero para el optimizador del compilador.Obviamente, las
register
sugerencias son similares a las sugerencias de probabilidad de ramificación proporcionadas por el usuario y se pueden inferir de estas sugerencias de probabilidad. Si el compilador sabe que alguna rama se toma con frecuencia, mantendrá las variables relacionadas con la rama en los registros. Por lo tanto, sugiero que se preocupe más por las sugerencias de rama y se olvideregister
. Idealmente, su generador de perfiles debería comunicarse de alguna manera con el compilador y evitar que usted piense siquiera en esos matices.fuente
A partir de gcc 9.3, compilación usando
-std=c++2a
,register
produce una advertencia del compilador, pero todavía tiene el efecto deseado y se comporta de forma idéntica a C deregister
al compilar sin -O1 - parámetros de optimización Ofast en el respeto de esta respuesta. Sin embargo, el uso de clang ++ - 7 provoca un error del compilador. Así que sí, lasregister
optimizaciones solo marcan la diferencia en la compilación estándar sin marcas de optimización -O, pero son optimizaciones básicas que el compilador descubriría incluso con -O1.La única diferencia es que en C ++, se le permite tomar la dirección de la variable de registro, lo que significa que la optimización solo ocurre si no toma la dirección de la variable o sus alias (para crear un puntero) o toma una referencia de él en el código (solo en - O0, porque una referencia también tiene una dirección, porque es un puntero constante en la pila , que, como un puntero, puede optimizarse fuera de la pila si se compila con -Ofast, excepto que nunca aparecerán en la pila usando -Ofast, porque a diferencia de un puntero, no se pueden hacer
volatile
y sus direcciones no se pueden tomar), de lo contrario se comportará como si no lo hubiera usadoregister
, y el valor se almacenará en la pila.En -O0, otra diferencia es que
const register
en gcc C y gcc C ++ no se comportan igual. En gcc C, seconst register
comporta comoregister
, porque block-scopeconst
s no está optimizado en gcc. En clang C,register
no hace nada y solo seconst
aplican optimizaciones de alcance de bloque. En gcc C, seregister
aplican optimizaciones peroconst
en el alcance del bloque no tiene optimización. En gcc C ++, se combinan las optimizaciones de alcance de bloqueregister
y deconst
bloque.#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
le dice al compilador que 1) almacene una variable local en un registro guardado de la persona que llama, en este casorbx
, y 2) optimice las escrituras de la pila si nunca se toma la dirección de la variable .const
le dice al compilador que sustituya el valor inmediatamente (en lugar de asignarle un registro o cargarlo desde la memoria) y escribir la variable local en la pila como comportamiento predeterminado.const register
es la combinación de estas optimizaciones envalentonadas. Esto es lo más delgado posible.Además, en gcc C y C ++,
register
por sí solo parece crear un espacio aleatorio de 16 bytes en la pila para el primer local en la pila, lo que no sucede conconst register
.Sin embargo, compilando usando -Ofast;
register
tiene un efecto de optimización 0 porque si se puede poner en un registro o inmediatamente, siempre lo será y si no, no lo será;const
todavía optimiza la carga en C y C ++ pero solo en el alcance del archivo ;volatile
todavía obliga a que los valores se almacenen y carguen desde la pila..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
fuente