@GMan: ANSI C ne permet pas de prendre l'adresse d'un objet registre; cette restriction ne s'applique pas au C ++
Brian R. Bondy
1
@Brian: Hm, tu as raison. C'est juste dans une note maintenant (qu'elle sera probablement ignorée si l'adresse est prise), mais pas obligatoire. Bon à savoir. (Eh bien, en quelque sorte.: P)
GManNickG
8
Le vote pour la réouverture registera une sémantique différente entre C et C ++.
CB Bailey
3
en conséquence, en C, il est possible d'interdire la conversion tableau en pointeur en créant un registre de tableau: register int a[1];avec cette déclaration, vous ne pouvez pas indexer ce tableau. Si vous essayez, vous faites UB
Johannes Schaub - litb
2
En effet, j'ai voté pour la réouverture. J'ai voté pour fermer avant de savoir qu'il y avait une différence.
GManNickG
Réponses:
24
En C ++ tel qu'il existait en 2010, tout programme valide qui utilise les mots-clés "auto" ou "register" sera sémantiquement identique à celui dont ces mots-clés sont supprimés (à moins qu'ils n'apparaissent dans des macros stringized ou d'autres contextes similaires). En ce sens, les mots-clés sont inutiles pour des programmes correctement compilés. D'un autre côté, les mots-clés peuvent être utiles dans certains contextes de macros pour garantir qu'une utilisation incorrecte d'une macro provoquera une erreur de compilation plutôt que de produire du faux code.
Dans C ++ 11 et les versions ultérieures du langage, le automot - clé a été redéfini pour agir comme un pseudo-type pour les objets qui sont initialisés, qu'un compilateur remplacera automatiquement par le type de l'expression d'initialisation. Ainsi, en C ++ 03, la déclaration: auto int i=(unsigned char)5;était équivalente à int i=5;lorsqu'elle était utilisée dans un contexte de bloc, et auto i=(unsigned char)5;était une violation de contrainte. En C ++ 11, auto int i=(unsigned char)5;est devenu une violation de contrainte alors que auto i=(unsigned char)5;est devenu équivalent à auto unsigned char i=5;.
Cette réponse n'est plus correcte, depuis 2011, le mot-clé autone peut pas être simplement omis ... Peut-être pourriez-vous mettre à jour votre réponse.
Walter le
2
@Walter: Pouvez-vous citer ce qui a changé? Je n'ai pas suivi tous les changements de langue.
supercat du
2
@supercat, oui, pour le moment, mais registerest obsolète et il va y avoir une proposition de le supprimer pour C ++ 17.
Jonathan Wakely
3
Selon en.cppreference.com/w/cpp/language/auto , post C ++ 11, autoest maintenant utilisé pour la déduction automatique de type Mais auparavant, il était utilisé pour spécifier que vous vouliez que votre variable soit stockée "automatiquement" ( donc sur la pile je suppose) par opposition au mot-clé register(signifiant "registre du processeur"):
Guillaume
97
register est un indice pour le compilateur, lui conseillant de stocker cette variable dans un registre de processeur au lieu de la mémoire (par exemple, au lieu de la pile).
Le compilateur peut suivre ou non cette indication.
Depuis C ++ 17, il est cependant obsolète, inutilisé et réservé.
ZachB
@ZachB, c'est incorrect; register est réservé en C ++ 17 mais il fonctionne toujours et fonctionne presque de la même manière que le registre de C.
Lewis Kelsey
@LewisKelsey Il est inutilisé et réservé dans la spécification C ++ 17; il ne fait pas partie de la storage-class-specifiergrammaire et n'a pas de sémantique définie. Un compilateur conforme peut générer une erreur comme le fait Clang. Néanmoins, certaines implémentations le permettent toujours et l'ignorent (MSVC, ICC) ou l'utilisent comme indice d'optimisation (GCC). Voir open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . J'ai cependant mal parlé sur un point: il était obsolète dans C ++ 11.
Avec les compilateurs d'aujourd'hui, probablement rien. C'était à l'origine un indice pour placer une variable dans un registre pour un accès plus rapide, mais la plupart des compilateurs aujourd'hui ignorent cet indice et décident par eux-mêmes.
registerest un indice pour le compilateur que vous prévoyez d'utiliser xbeaucoup, et que vous pensez qu'il devrait être placé dans un registre.
Cependant, les compilateurs sont maintenant bien meilleurs pour déterminer les valeurs à placer dans les registres que le programmeur moyen (ou même expert), donc les compilateurs ignorent simplement le mot-clé et font ce qu'ils veulent.
Et j'ajouterais que le mot-clé «register» ne serait utile que sur un microcontrôleur exécutant un seul programme C ++ sans thread et sans multi-tâches. Le programme C ++ devrait posséder la totalité du CPU pour s'assurer que la variable «register» ne sera pas déplacée des registres CPU spéciaux.
Santiago Villafuerte
@SantiagoVillafuerte voulez-vous l'ajouter en modifiant la réponse?
ncomputers
Je ne suis pas sûr de ma réponse ... même si cela semble plausible. Je préfère le laisser comme un commentaire afin que les autres l'approuvent ou le désapprouvent.
Santiago Villafuerte
1
@SantiagoVillafuerte Ce n'est pas vrai, dans les systèmes multitâches, lorsque le changement de contexte est le système d'exploitation - pas l'application - est responsable de la sauvegarde / restauration des registres. Comme vous ne changez pas de contexte après chaque instruction du processeur, mettre des choses dans des registres est absolument significatif. Les autres réponses ici (que les compilateurs ne se soucient tout simplement pas de votre opinion en ce qui concerne l'attribution des registres) sont plus précises.
Cubic
L'exemple que vous avez montré utilise en fait l'extension Explicit Register Variables de GCC , qui est différente du registerspécificateur de classe de stockage et est toujours prise en charge par GCC.
ZachB le
1
Prenons un cas où l'optimiseur du compilateur a deux variables et est forcé d'en renverser une sur la pile. Il se trouve que les deux variables ont le même poids pour le compilateur. Étant donné qu'il n'y a pas de différence, le compilateur renverra arbitrairement l'une des variables. D'un autre côté, le registermot - clé donne au compilateur une indication sur la variable qui sera consultée plus fréquemment. Elle est similaire à l'instruction de prélecture x86, mais pour l'optimiseur du compilateur.
De toute évidence, les registerindices sont similaires aux indices de probabilité de branche fournis par l'utilisateur et peuvent être déduits de ces indices de probabilité. Si le compilateur sait qu'une branche est souvent prise, il conservera les variables liées à la branche dans les registres. Je suggère donc de se soucier davantage des indices de branche et d'oublier register. Idéalement, votre profileur devrait communiquer d'une manière ou d'une autre avec le compilateur et vous éviter même de penser à de telles nuances.
Depuis gcc 9.3, la compilation en utilisant -std=c++2a,register produit un avertissement du compilateur, mais il a toujours l'effet désiré et se comporte de la même manière que les C registerlors de la compilation sans les indicateurs d'optimisation -O1 –- Ofast dans le respect de cette réponse. L'utilisation de clang ++ - 7 provoque cependant une erreur du compilateur. Donc oui,register optimisations ne font une différence que sur la compilation standard sans indicateur d'optimisation -O, mais ce sont des optimisations de base que le compilateur trouverait même avec -O1.
La seule différence est qu'en C ++, vous êtes autorisé à prendre l'adresse de la variable de registre ce qui signifie que l'optimisation ne se produit que si vous ne prenez pas l'adresse de la variable ou ses alias (pour créer un pointeur) ou prenez une référence de celui-ci dans le code (uniquement sur - O0, car une référence a également une adresse, car c'est un pointeur const sur la pile , qui, comme un pointeur peut être optimisé hors de la pile en cas de compilation avec -Ofast, sauf n'apparaîtront jamais sur la pile en utilisant -Ofast, car contrairement à un pointeur, ils ne peuvent pas être créés volatileet leurs adresses ne peuvent pas être prises), sinon il se comportera comme vous ne l'aviez pas utilisé register, et la valeur sera stockée sur la pile.
Sur -O0, une autre différence est que const registersur gcc C et gcc C ++ ne se comportent pas de la même manière. Sur gcc C, const registerse comporte comme register, car les portées de bloc constne sont pas optimisées sur gcc. Sur clang C, registerne fait rien et seules constles optimisations de type bloc s'appliquent. Sur gcc C, les registeroptimisations s'appliquent mais constà block-scope n'a pas d'optimisation. Sur gcc C ++, les optimisations registeret constles optimisations de type bloc se combinent.
#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
registerdit au compilateur de 1) stocker une variable locale dans un registre sauvegardé appelé, dans ce cas rbx, et 2) d' optimiser les écritures de pile si l'adresse de la variable n'est jamais prise . constdit au compilateur de remplacer la valeur immédiatement (au lieu de lui attribuer un registre ou de le charger à partir de la mémoire) et d'écrire la variable locale dans la pile comme comportement par défaut. const registerest la combinaison de ces optimisations enhardies. C'est aussi mince que possible.
De plus, sur gcc C et C ++, registersemble créer à lui seul un écart aléatoire de 16 octets sur la pile pour le premier local de la pile, ce qui ne se produit pas avec const register.
Compiler en utilisant -Ofast cependant; registera un effet d'optimisation nul car s'il peut être mis dans un registre ou rendu immédiat, il le sera toujours et s'il ne le peut pas, il ne le sera pas; constoptimise toujours la charge sur C et C ++ mais au niveau du fichier uniquement ; volatileforce toujours le stockage et le chargement des valeurs à partir de la pile.
.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
a une sémantique différente entre C et C ++.register int a[1];
avec cette déclaration, vous ne pouvez pas indexer ce tableau. Si vous essayez, vous faites UBRéponses:
En C ++ tel qu'il existait en 2010, tout programme valide qui utilise les mots-clés "auto" ou "register" sera sémantiquement identique à celui dont ces mots-clés sont supprimés (à moins qu'ils n'apparaissent dans des macros stringized ou d'autres contextes similaires). En ce sens, les mots-clés sont inutiles pour des programmes correctement compilés. D'un autre côté, les mots-clés peuvent être utiles dans certains contextes de macros pour garantir qu'une utilisation incorrecte d'une macro provoquera une erreur de compilation plutôt que de produire du faux code.
Dans C ++ 11 et les versions ultérieures du langage, le
auto
mot - clé a été redéfini pour agir comme un pseudo-type pour les objets qui sont initialisés, qu'un compilateur remplacera automatiquement par le type de l'expression d'initialisation. Ainsi, en C ++ 03, la déclaration:auto int i=(unsigned char)5;
était équivalente àint i=5;
lorsqu'elle était utilisée dans un contexte de bloc, etauto i=(unsigned char)5;
était une violation de contrainte. En C ++ 11,auto int i=(unsigned char)5;
est devenu une violation de contrainte alors queauto i=(unsigned char)5;
est devenu équivalent àauto unsigned char i=5;
.la source
auto
ne peut pas être simplement omis ... Peut-être pourriez-vous mettre à jour votre réponse.register
est obsolète et il va y avoir une proposition de le supprimer pour C ++ 17.auto
est maintenant utilisé pour la déduction automatique de type Mais auparavant, il était utilisé pour spécifier que vous vouliez que votre variable soit stockée "automatiquement" ( donc sur la pile je suppose) par opposition au mot-cléregister
(signifiant "registre du processeur"):register
est un indice pour le compilateur, lui conseillant de stocker cette variable dans un registre de processeur au lieu de la mémoire (par exemple, au lieu de la pile).Le compilateur peut suivre ou non cette indication.
Selon Herb Sutter dans "Mots clés qui ne sont pas (ou, commentaires d'un autre nom)" :
la source
storage-class-specifier
grammaire et n'a pas de sémantique définie. Un compilateur conforme peut générer une erreur comme le fait Clang. Néanmoins, certaines implémentations le permettent toujours et l'ignorent (MSVC, ICC) ou l'utilisent comme indice d'optimisation (GCC). Voir open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . J'ai cependant mal parlé sur un point: il était obsolète dans C ++ 11.Selon Herb Sutter ,
register
est " exactement aussi significatif que les espaces " et n'a aucun effet sur la sémantique d'un programme C ++.la source
Avec les compilateurs d'aujourd'hui, probablement rien. C'était à l'origine un indice pour placer une variable dans un registre pour un accès plus rapide, mais la plupart des compilateurs aujourd'hui ignorent cet indice et décident par eux-mêmes.
la source
Presque certainement rien.
register
est un indice pour le compilateur que vous prévoyez d'utiliserx
beaucoup, et que vous pensez qu'il devrait être placé dans un registre.Cependant, les compilateurs sont maintenant bien meilleurs pour déterminer les valeurs à placer dans les registres que le programmeur moyen (ou même expert), donc les compilateurs ignorent simplement le mot-clé et font ce qu'ils veulent.
la source
register
est obsolète dans C ++ 11. Il est inutilisé et réservé en C ++ 17.Source: http://en.cppreference.com/w/cpp/keyword/register
la source
Le
register
mot-clé était utile pour:Un exemple de système productif, où le
register
mot - clé était requis:typedef unsigned long long Out; volatile Out out,tmp; Out register rax asm("rax"); asm volatile("rdtsc":"=A"(rax)); out=out*tmp+rax;
Il est obsolète depuis C ++ 11 et est inutilisé et réservé dans C ++ 17 .
la source
register
spécificateur de classe de stockage et est toujours prise en charge par GCC.Prenons un cas où l'optimiseur du compilateur a deux variables et est forcé d'en renverser une sur la pile. Il se trouve que les deux variables ont le même poids pour le compilateur. Étant donné qu'il n'y a pas de différence, le compilateur renverra arbitrairement l'une des variables. D'un autre côté, le
register
mot - clé donne au compilateur une indication sur la variable qui sera consultée plus fréquemment. Elle est similaire à l'instruction de prélecture x86, mais pour l'optimiseur du compilateur.De toute évidence, les
register
indices sont similaires aux indices de probabilité de branche fournis par l'utilisateur et peuvent être déduits de ces indices de probabilité. Si le compilateur sait qu'une branche est souvent prise, il conservera les variables liées à la branche dans les registres. Je suggère donc de se soucier davantage des indices de branche et d'oublierregister
. Idéalement, votre profileur devrait communiquer d'une manière ou d'une autre avec le compilateur et vous éviter même de penser à de telles nuances.la source
Depuis gcc 9.3, la compilation en utilisant
-std=c++2a
,register
produit un avertissement du compilateur, mais il a toujours l'effet désiré et se comporte de la même manière que les Cregister
lors de la compilation sans les indicateurs d'optimisation -O1 –- Ofast dans le respect de cette réponse. L'utilisation de clang ++ - 7 provoque cependant une erreur du compilateur. Donc oui,register
optimisations ne font une différence que sur la compilation standard sans indicateur d'optimisation -O, mais ce sont des optimisations de base que le compilateur trouverait même avec -O1.La seule différence est qu'en C ++, vous êtes autorisé à prendre l'adresse de la variable de registre ce qui signifie que l'optimisation ne se produit que si vous ne prenez pas l'adresse de la variable ou ses alias (pour créer un pointeur) ou prenez une référence de celui-ci dans le code (uniquement sur - O0, car une référence a également une adresse, car c'est un pointeur const sur la pile , qui, comme un pointeur peut être optimisé hors de la pile en cas de compilation avec -Ofast, sauf n'apparaîtront jamais sur la pile en utilisant -Ofast, car contrairement à un pointeur, ils ne peuvent pas être créés
volatile
et leurs adresses ne peuvent pas être prises), sinon il se comportera comme vous ne l'aviez pas utiliséregister
, et la valeur sera stockée sur la pile.Sur -O0, une autre différence est que
const register
sur gcc C et gcc C ++ ne se comportent pas de la même manière. Sur gcc C,const register
se comporte commeregister
, car les portées de blocconst
ne sont pas optimisées sur gcc. Sur clang C,register
ne fait rien et seulesconst
les optimisations de type bloc s'appliquent. Sur gcc C, lesregister
optimisations s'appliquent maisconst
à block-scope n'a pas d'optimisation. Sur gcc C ++, les optimisationsregister
etconst
les optimisations de type bloc se combinent.#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
dit au compilateur de 1) stocker une variable locale dans un registre sauvegardé appelé, dans ce casrbx
, et 2) d' optimiser les écritures de pile si l'adresse de la variable n'est jamais prise .const
dit au compilateur de remplacer la valeur immédiatement (au lieu de lui attribuer un registre ou de le charger à partir de la mémoire) et d'écrire la variable locale dans la pile comme comportement par défaut.const register
est la combinaison de ces optimisations enhardies. C'est aussi mince que possible.De plus, sur gcc C et C ++,
register
semble créer à lui seul un écart aléatoire de 16 octets sur la pile pour le premier local de la pile, ce qui ne se produit pas avecconst register
.Compiler en utilisant -Ofast cependant;
register
a un effet d'optimisation nul car s'il peut être mis dans un registre ou rendu immédiat, il le sera toujours et s'il ne le peut pas, il ne le sera pas;const
optimise toujours la charge sur C et C ++ mais au niveau du fichier uniquement ;volatile
force toujours le stockage et le chargement des valeurs à partir de la pile..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
la source