@GMan: ANSI C erlaubt nicht, die Adresse eines Registerobjekts zu übernehmen. Diese Einschränkung gilt nicht für C ++
Brian R. Bondy
1
@ Brian: Hm, du hast recht. Es ist jetzt nur in einer Notiz (dass es wahrscheinlich ignoriert wird, wenn die Adresse genommen wird), aber nicht vorgeschrieben. Gut zu wissen. (Nun, irgendwie .: P)
GManNickG
8
Abstimmung zur Wiedereröffnung register hat eine unterschiedliche Semantik zwischen C und C ++.
CB Bailey
3
Infolgedessen ist es in C möglich, die Umwandlung von Array in Zeiger zu verbieten, indem ein Array-Register erstellt wird: register int a[1];Mit dieser Deklaration können Sie dieses Array nicht indizieren. Wenn Sie es versuchen, machen Sie UB
Johannes Schaub - litb
2
In der Tat habe ich für die Wiedereröffnung gestimmt. Ich habe für das Schließen gestimmt, bevor ich wusste, dass es einen Unterschied gibt.
GManNickG
Antworten:
25
In C ++, wie es 2010 existierte, ist jedes gültige Programm, das die Schlüsselwörter "auto" oder "register" verwendet, semantisch identisch mit einem Programm, bei dem diese Schlüsselwörter entfernt wurden (es sei denn, sie erscheinen in stringierten Makros oder anderen ähnlichen Kontexten). In diesem Sinne sind die Schlüsselwörter für das ordnungsgemäße Kompilieren von Programmen unbrauchbar. Andererseits können die Schlüsselwörter in bestimmten Makrokontexten nützlich sein, um sicherzustellen, dass eine unsachgemäße Verwendung eines Makros einen Fehler bei der Kompilierung verursacht, anstatt falschen Code zu erzeugen.
In C ++ 11 und späteren Versionen der Sprache wurde das autoSchlüsselwort neu definiert, um als Pseudotyp für initialisierte Objekte zu fungieren, die ein Compiler automatisch durch den Typ des initialisierenden Ausdrucks ersetzt. Daher war in C ++ 03 die Deklaration: auto int i=(unsigned char)5;äquivalent zu, int i=5;wenn sie in einem Blockkontext verwendet wurde, und auto i=(unsigned char)5;war eine Einschränkungsverletzung. In C ++ 11 auto int i=(unsigned char)5;wurde eine Einschränkungsverletzung, während sie auto i=(unsigned char)5;äquivalent zu wurde auto unsigned char i=5;.
Ein Beispiel für das letzte Bit kann nützlich sein.
Dennis Zickefoose
14
Diese Antwort ist nicht mehr korrekt, da das Schlüsselwort seit 2011 autonicht einfach weggelassen werden kann ... Vielleicht können Sie Ihre Antwort aktualisieren.
Walter
2
@Walter: Kannst du zitieren, was sich geändert hat? Ich habe nicht alle Sprachänderungen verfolgt.
Supercat
2
@supercat, ja, im Moment, aber registerveraltet und es wird einen Vorschlag geben, es für C ++ 17 zu entfernen.
Jonathan Wakely
3
Laut en.cppreference.com/w/cpp/language/auto wird Post C ++ 11 autojetzt für den automatischen Typabzug verwendet. Zuvor wurde jedoch angegeben, dass Ihre Variable "automatisch" gespeichert werden soll ( daher auf dem Stapel, denke ich) im Gegensatz zum Schlüsselwort register(was "Prozessorregister" bedeutet):
Guillaume
96
register ist ein Hinweis für den Compiler, der ihm empfiehlt, diese Variable in einem Prozessorregister anstelle des Speichers (z. B. anstelle des Stapels) zu speichern.
Der Compiler kann diesem Hinweis folgen oder nicht.
Seit C ++ 17 ist es jedoch veraltet, unbenutzt und reserviert.
ZachB
@ZachB, das ist falsch; Register wird in C ++ reserviert 17 , aber es immer noch funktioniert und funktioniert fast genauso C des Registers.
Lewis Kelsey
@ LewisKelsey Es wird nicht verwendet und ist in der C ++ 17-Spezifikation reserviert. Es gehört nicht storage-class-specifierzur Grammatik und hat keine definierte Semantik. Ein konformer Compiler kann wie Clang einen Fehler auslösen. Einige Implementierungen erlauben es jedoch weiterhin und ignorieren es entweder (MSVC, ICC) oder verwenden es als Optimierungshinweis (GCC). Siehe open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . Ich habe jedoch in einem Punkt falsch gesprochen: Es war in C ++ 11 veraltet.
Mit den heutigen Compilern wahrscheinlich nichts. Es war ursprünglich ein Hinweis, eine Variable für einen schnelleren Zugriff in ein Register zu stellen, aber die meisten Compiler ignorieren diesen Hinweis heute und entscheiden selbst.
register ist ein Hinweis auf den Compiler, den Sie verwenden möchten x viel möchten und dass Sie der Meinung sind, dass es in ein Register gestellt werden sollte.
Compiler können jetzt jedoch weitaus besser bestimmen, welche Werte in Registern abgelegt werden sollen als der durchschnittliche (oder sogar erfahrene) Programmierer. Daher ignorieren Compiler das Schlüsselwort einfach und tun, was sie wollen.
Und ich würde hinzufügen, dass das Schlüsselwort 'register' nur auf einem Mikrocontroller nützlich wäre, auf dem ein einzelnes C ++ - Programm ohne Threads und ohne Multitasking ausgeführt wird. Das C ++ - Programm müsste die gesamte CPU besitzen, um sicherzustellen, dass die Variable 'register' nicht aus den speziellen CPU-Registern verschoben wird.
Santiago Villafuerte
@ SantiagoVillafuerte Möchten Sie es hinzufügen, um die Antwort zu bearbeiten?
ncomputers
Ich bin mir meiner Antwort nicht so sicher ... obwohl es plausibel klingt. Ich würde es vorziehen, es als Kommentar zu hinterlassen, damit andere es genehmigen oder ablehnen.
Santiago Villafuerte
1
@SantiagoVillafuerte Dies ist in Multitasking-Systemen nicht der Fall, wenn die Kontextumschaltung des Betriebssystems - nicht der Anwendung - für das Speichern / Wiederherstellen von Registern verantwortlich ist. Da Sie nicht nach jedem CPU-Befehl den Kontext wechseln, ist es absolut sinnvoll, Dinge in Register zu setzen. Die anderen Antworten hier (dass Compiler sich einfach nicht um Ihre Meinung kümmern, wenn es um die Zuweisung von Registern geht) sind genauer.
Cubic
Das Beispiel, das Sie gezeigt haben, verwendet tatsächlich die Erweiterung Explicit Register Variablesregister von GCC , die sich vom Speicherklassenspezifizierer unterscheidet und weiterhin von GCC unterstützt wird.
ZachB
1
Stellen Sie sich einen Fall vor, in dem der Optimierer des Compilers zwei Variablen enthält und gezwungen ist, eine auf den Stapel zu übertragen. So kam es, dass beide Variablen für den Compiler das gleiche Gewicht haben. Da es keinen Unterschied gibt, verschüttet der Compiler willkürlich eine der Variablen. Andererseits gibt das registerSchlüsselwort dem Compiler einen Hinweis, auf welche Variable häufiger zugegriffen wird. Es ähnelt der x86-Prefetch-Anweisung, ist jedoch für den Compiler-Optimierer vorgesehen.
Offensichtlich registerähneln Hinweise den vom Benutzer bereitgestellten Verzweigungswahrscheinlichkeitshinweisen und können aus diesen Wahrscheinlichkeitshinweisen abgeleitet werden. Wenn der Compiler weiß, dass ein Zweig häufig verwendet wird, werden verzweigungsbezogene Variablen in Registern gespeichert. Deshalb schlage ich vor, mich mehr um Zweighinweise zu kümmern und diese zu vergessen register. Idealerweise sollte Ihr Profiler irgendwie mit dem Compiler kommunizieren und Sie davon abhalten, über solche Nuancen nachzudenken.
Ab gcc 9.3 kompilieren mit -std=c++2a,register erzeugt eine Compiler - Warnung, aber es hat immer noch die gewünschte Wirkung und verhält sich identisch zu C ist , registerwenn sie ohne -O1 Kompilieren - Ofast Optimierungen im Hinblick auf diese Antwort. Die Verwendung von clang ++ - 7 verursacht jedoch einen Compilerfehler. Also ja,register Optimierungen machen nur bei der Standardkompilierung ohne Optimierungs -O-Flags einen Unterschied, aber es handelt sich um grundlegende Optimierungen, die der Compiler selbst mit -O1 herausfinden würde.
Der einzige Unterschied besteht darin, dass Sie in C ++ die Adresse der Registervariablen übernehmen dürfen. Dies bedeutet, dass die Optimierung nur erfolgt, wenn Sie nicht die Adresse der Variablen oder ihrer Aliase (um einen Zeiger zu erstellen) oder eine Referenz verwenden davon im Code (nur bei - O0, da eine Referenz hat auch eine Adresse, weil es ein const Zeiger auf dem Stapel ist , der, wie ein Zeiger kann vom Stapel optimiert werden , wenn mit -Ofast kompilieren, außer sie werden nie angezeigt auf dem Stapel mit -Ofast, da sie im Gegensatz zu einem Zeiger nicht erstellt volatileund ihre Adressen nicht verwendet werden können), andernfalls verhält es sich so, als hätten Sie sie nicht verwendetregister , und der Wert wird auf dem Stapel gespeichert.
Bei -O0 besteht ein weiterer Unterschied darin, dass sich const registergcc C und gcc C ++ nicht gleich verhalten. Verhält sich auf gcc C const registerwie folgt register, da Blockbereiche constauf gcc nicht optimiert sind. Bei Clang C gelten registernichts und nur constBlock-Scope-Optimierungen. Auf gcc C registergelten Optimierungen, constim Blockbereich jedoch keine Optimierung. In gcc C ++ werden sowohl Optimierungen registerals auch constBlockbereichsoptimierungen kombiniert.
#include<stdio.h>//yes it's C code on C++int main(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
registerweist den Compiler an, 1) in diesem Fall eine lokale Variable in einem von einem Angerufenen gespeicherten Register zu speichern rbxund 2) Stapelschreibvorgänge zu optimieren, wenn die Adresse der Variablen niemals verwendet wird . constWeist den Compiler an, den Wert sofort zu ersetzen (anstatt ihm ein Register zuzuweisen oder ihn aus dem Speicher zu laden) und die lokale Variable als Standardverhalten in den Stapel zu schreiben. const registerist die Kombination dieser ermutigten Optimierungen. Dies ist so schlank wie es nur geht.
Außerdem registerscheint es unter gcc C und C ++ für sich genommen eine zufällige 16-Byte-Lücke im Stapel für das erste lokale Element im Stapel zu geben, was bei nicht der Fall ist const register.
Kompilieren mit -Ofast jedoch; registerhat 0 Optimierungseffekt, denn wenn es in ein Register aufgenommen oder sofort erstellt werden kann, wird es immer sein und wenn es nicht kann, wird es nicht sein; constoptimiert weiterhin die Auslastung von C und C ++, jedoch nur im Dateibereich ; volatileErzwingt weiterhin, dass die Werte gespeichert und vom Stapel geladen werden.
.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
register
hat eine unterschiedliche Semantik zwischen C und C ++.register int a[1];
Mit dieser Deklaration können Sie dieses Array nicht indizieren. Wenn Sie es versuchen, machen Sie UBAntworten:
In C ++, wie es 2010 existierte, ist jedes gültige Programm, das die Schlüsselwörter "auto" oder "register" verwendet, semantisch identisch mit einem Programm, bei dem diese Schlüsselwörter entfernt wurden (es sei denn, sie erscheinen in stringierten Makros oder anderen ähnlichen Kontexten). In diesem Sinne sind die Schlüsselwörter für das ordnungsgemäße Kompilieren von Programmen unbrauchbar. Andererseits können die Schlüsselwörter in bestimmten Makrokontexten nützlich sein, um sicherzustellen, dass eine unsachgemäße Verwendung eines Makros einen Fehler bei der Kompilierung verursacht, anstatt falschen Code zu erzeugen.
In C ++ 11 und späteren Versionen der Sprache wurde das
auto
Schlüsselwort neu definiert, um als Pseudotyp für initialisierte Objekte zu fungieren, die ein Compiler automatisch durch den Typ des initialisierenden Ausdrucks ersetzt. Daher war in C ++ 03 die Deklaration:auto int i=(unsigned char)5;
äquivalent zu,int i=5;
wenn sie in einem Blockkontext verwendet wurde, undauto i=(unsigned char)5;
war eine Einschränkungsverletzung. In C ++ 11auto int i=(unsigned char)5;
wurde eine Einschränkungsverletzung, während sieauto i=(unsigned char)5;
äquivalent zu wurdeauto unsigned char i=5;
.quelle
auto
nicht einfach weggelassen werden kann ... Vielleicht können Sie Ihre Antwort aktualisieren.register
veraltet und es wird einen Vorschlag geben, es für C ++ 17 zu entfernen.auto
jetzt für den automatischen Typabzug verwendet. Zuvor wurde jedoch angegeben, dass Ihre Variable "automatisch" gespeichert werden soll ( daher auf dem Stapel, denke ich) im Gegensatz zum Schlüsselwortregister
(was "Prozessorregister" bedeutet):register
ist ein Hinweis für den Compiler, der ihm empfiehlt, diese Variable in einem Prozessorregister anstelle des Speichers (z. B. anstelle des Stapels) zu speichern.Der Compiler kann diesem Hinweis folgen oder nicht.
Laut Herb Sutter in "Keywords That Aren't (oder Kommentare von einem anderen Namen)" :
quelle
storage-class-specifier
zur Grammatik und hat keine definierte Semantik. Ein konformer Compiler kann wie Clang einen Fehler auslösen. Einige Implementierungen erlauben es jedoch weiterhin und ignorieren es entweder (MSVC, ICC) oder verwenden es als Optimierungshinweis (GCC). Siehe open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . Ich habe jedoch in einem Punkt falsch gesprochen: Es war in C ++ 11 veraltet.Nach Herb Sutter ,
register
ist „ genau so sinnvoll wie Leerzeichen “ und hat keine Auswirkungen auf die Semantik eines C ++ Programm.quelle
Mit den heutigen Compilern wahrscheinlich nichts. Es war ursprünglich ein Hinweis, eine Variable für einen schnelleren Zugriff in ein Register zu stellen, aber die meisten Compiler ignorieren diesen Hinweis heute und entscheiden selbst.
quelle
Mit ziemlicher Sicherheit nichts.
register
ist ein Hinweis auf den Compiler, den Sie verwenden möchtenx
viel möchten und dass Sie der Meinung sind, dass es in ein Register gestellt werden sollte.Compiler können jetzt jedoch weitaus besser bestimmen, welche Werte in Registern abgelegt werden sollen als der durchschnittliche (oder sogar erfahrene) Programmierer. Daher ignorieren Compiler das Schlüsselwort einfach und tun, was sie wollen.
quelle
register
ist in C ++ 11 veraltet. Es wird in C ++ 17 nicht verwendet und reserviert.Quelle: http://en.cppreference.com/w/cpp/keyword/register
quelle
Das
register
Schlüsselwort war nützlich für:Ein Beispiel für ein Produktivsystem, bei dem das
register
Schlüsselwort erforderlich war:Es ist seit C ++ 11 veraltet und wird in C ++ 17 nicht verwendet und reserviert .
quelle
register
von GCC , die sich vom Speicherklassenspezifizierer unterscheidet und weiterhin von GCC unterstützt wird.Stellen Sie sich einen Fall vor, in dem der Optimierer des Compilers zwei Variablen enthält und gezwungen ist, eine auf den Stapel zu übertragen. So kam es, dass beide Variablen für den Compiler das gleiche Gewicht haben. Da es keinen Unterschied gibt, verschüttet der Compiler willkürlich eine der Variablen. Andererseits gibt das
register
Schlüsselwort dem Compiler einen Hinweis, auf welche Variable häufiger zugegriffen wird. Es ähnelt der x86-Prefetch-Anweisung, ist jedoch für den Compiler-Optimierer vorgesehen.Offensichtlich
register
ähneln Hinweise den vom Benutzer bereitgestellten Verzweigungswahrscheinlichkeitshinweisen und können aus diesen Wahrscheinlichkeitshinweisen abgeleitet werden. Wenn der Compiler weiß, dass ein Zweig häufig verwendet wird, werden verzweigungsbezogene Variablen in Registern gespeichert. Deshalb schlage ich vor, mich mehr um Zweighinweise zu kümmern und diese zu vergessenregister
. Idealerweise sollte Ihr Profiler irgendwie mit dem Compiler kommunizieren und Sie davon abhalten, über solche Nuancen nachzudenken.quelle
Ab gcc 9.3 kompilieren mit
-std=c++2a
,register
erzeugt eine Compiler - Warnung, aber es hat immer noch die gewünschte Wirkung und verhält sich identisch zu C ist ,register
wenn sie ohne -O1 Kompilieren - Ofast Optimierungen im Hinblick auf diese Antwort. Die Verwendung von clang ++ - 7 verursacht jedoch einen Compilerfehler. Also ja,register
Optimierungen machen nur bei der Standardkompilierung ohne Optimierungs -O-Flags einen Unterschied, aber es handelt sich um grundlegende Optimierungen, die der Compiler selbst mit -O1 herausfinden würde.Der einzige Unterschied besteht darin, dass Sie in C ++ die Adresse der Registervariablen übernehmen dürfen. Dies bedeutet, dass die Optimierung nur erfolgt, wenn Sie nicht die Adresse der Variablen oder ihrer Aliase (um einen Zeiger zu erstellen) oder eine Referenz verwenden davon im Code (nur bei - O0, da eine Referenz hat auch eine Adresse, weil es ein const Zeiger auf dem Stapel ist , der, wie ein Zeiger kann vom Stapel optimiert werden , wenn mit -Ofast kompilieren, außer sie werden nie angezeigt auf dem Stapel mit -Ofast, da sie im Gegensatz zu einem Zeiger nicht erstellt
volatile
und ihre Adressen nicht verwendet werden können), andernfalls verhält es sich so, als hätten Sie sie nicht verwendetregister
, und der Wert wird auf dem Stapel gespeichert.Bei -O0 besteht ein weiterer Unterschied darin, dass sich
const register
gcc C und gcc C ++ nicht gleich verhalten. Verhält sich auf gcc Cconst register
wie folgtregister
, da Blockbereicheconst
auf gcc nicht optimiert sind. Bei Clang C geltenregister
nichts und nurconst
Block-Scope-Optimierungen. Auf gcc Cregister
gelten Optimierungen,const
im Blockbereich jedoch keine Optimierung. In gcc C ++ werden sowohl Optimierungenregister
als auchconst
Blockbereichsoptimierungen kombiniert.int i = 3;
::register int i = 3;
::const int i = 3;
const register int i = 3;
register
weist den Compiler an, 1) in diesem Fall eine lokale Variable in einem von einem Angerufenen gespeicherten Register zu speichernrbx
und 2) Stapelschreibvorgänge zu optimieren, wenn die Adresse der Variablen niemals verwendet wird .const
Weist den Compiler an, den Wert sofort zu ersetzen (anstatt ihm ein Register zuzuweisen oder ihn aus dem Speicher zu laden) und die lokale Variable als Standardverhalten in den Stapel zu schreiben.const register
ist die Kombination dieser ermutigten Optimierungen. Dies ist so schlank wie es nur geht.Außerdem
register
scheint es unter gcc C und C ++ für sich genommen eine zufällige 16-Byte-Lücke im Stapel für das erste lokale Element im Stapel zu geben, was bei nicht der Fall istconst register
.Kompilieren mit -Ofast jedoch;
register
hat 0 Optimierungseffekt, denn wenn es in ein Register aufgenommen oder sofort erstellt werden kann, wird es immer sein und wenn es nicht kann, wird es nicht sein;const
optimiert weiterhin die Auslastung von C und C ++, jedoch nur im Dateibereich ;volatile
Erzwingt weiterhin, dass die Werte gespeichert und vom Stapel geladen werden.quelle