違いは何ですか
int x=7;
そして
register int x=7;
?
私はC ++を使用しています。
違いは何ですか
int x=7;
そして
register int x=7;
?
私はC ++を使用しています。
registerは、CとC ++の間で異なるセマンティクスを持っています。
register int a[1];ことができます。その宣言では、その配列にインデックスを付けることはできません。試してみると、UB
回答:
2010年に存在したC ++では、キーワード「auto」または「register」を使用する有効なプログラムは、それらのキーワードが削除されたものと意味的に同一になります(文字列化されたマクロまたは他の同様のコンテキストに表示されない限り)。その意味で、キーワードはプログラムを適切にコンパイルするのに役に立たない。一方、キーワードは、特定のマクロコンテキストで、マクロの不適切な使用が偽のコードを生成するのではなく、コンパイル時エラーを引き起こすことを保証するのに役立つ場合があります。
言語のC ++ 11以降のバージョンでは、autoキーワードは、初期化されるオブジェクトの疑似型として機能するように再利用されました。これは、コンパイラーが初期化式の型に自動的に置き換えます。したがって、C ++ 03では、宣言:auto int i=(unsigned char)5;はint i=5;ブロックコンテキスト内で使用された場合と同等でありauto i=(unsigned char)5;、制約違反でした。C ++ 11では、と同等にauto int i=(unsigned char)5;なりましたが、制約違反にauto i=(unsigned char)5;なりましたauto unsigned char i=5;。
autoを単純に省略することはできません...おそらく回答を更新できます。
registerはありますが、非推奨であり、C ++ 17用に削除する提案があります。
autoは自動型推定に使用されるようになりましたが、それ以前は、変数を「自動的に」保存することを指定するために使用されていました(したがって、キーワード(「プロセッサのレジスタ」を意味する)とは対照的に、スタック上にあると思いますregister:
register はコンパイラへのヒントであり、その変数をメモリではなく(たとえば、スタックではなく)プロセッサレジスタに格納するようにアドバイスします。
コンパイラは、そのヒントに従う場合と従わない場合があります。
「そうではないキーワード(または、別の名前によるコメント)」のハーブサッターによると:
レジスタ指定子は、自動指定子と同じセマンティクスを持っています。
storage-class-specifier文法の1つではなく、定義されたセマンティクスもありません。準拠するコンパイラは、Clangのようにエラーをスローする可能性があります。それでも、一部の実装ではそれが許可され、無視されるか(MSVC、ICC)、最適化のヒントとして使用されます(GCC)。open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.htmlを参照してください。しかし、私はある点でミスピークをしました:それはC ++ 11で非推奨になりました。
Herb Sutterによると、registerは「空白とまったく同じ意味」であり、C ++プログラムのセマンティクスには影響しません。
registerC ++ 11では非推奨です。未使用で、C ++ 17で予約されています。
registerキーワードが有用でした:
registerキーワードが必要な生産システムの例:
typedef unsigned long long Out;
volatile Out out,tmp;
Out register rax asm("rax");
asm volatile("rdtsc":"=A"(rax));
out=out*tmp+rax;
これはC ++ 11以降非推奨であり、C ++ 17では使用および予約されていません。
registerストレージクラス指定子とは異なり、GCCでも引き続きサポートされています。
コンパイラのオプティマイザに2つの変数があり、1つをスタックに強制的にスピルする場合を考えてみます。たまたま、両方の変数がコンパイラに対して同じ重みを持っています。違いがない場合、コンパイラーは変数の1つを任意にこぼします。一方、registerキーワードは、どの変数がより頻繁にアクセスされるかについてのヒントをコンパイラーに提供します。これはx86プリフェッチ命令に似ていますが、コンパイラオプティマイザ用です。
明らかに、registerヒントはユーザーが提供する分岐確率ヒントに似ており、これらの確率ヒントから推測できます。コンパイラーは、ある分岐が頻繁に行われることを知っている場合、分岐関連の変数をレジスターに保持します。ですから、ブランチのヒントをもっと気にかけ、を忘れることをお勧めしregisterます。理想的には、プロファイラーはコンパイラーと何らかの形で通信し、そのようなニュアンスについて考えることさえしないようにする必要があります。
GCC 9.3の時点で、使用してコンパイルし-std=c++2a、 registerコンパイラの警告を生成し、それはまだCのと同じに所望の効果および振る舞いを持っていregister-O1せずにコンパイルするとき- Ofast最適化フラグの点で、この答え。ただし、clang ++-7を使用すると、コンパイラエラーが発生します。あ、はい、register最適化は最適化-Oフラグのない標準的なコンパイルでのみ違いを生みますが、それらはコンパイラーが-O1を使用しても理解する基本的な最適化です。
唯一の違いは、C ++では、レジスタ変数のアドレスを取得できることです。つまり、変数またはそのエイリアスのアドレス(ポインタを作成するため)を取得しないか、参照を取得しない場合にのみ最適化が行われます。コード内のそれの(-O0でのみ、参照にもアドレスがあるため、スタック上のconstポインターであるため、-Ofastを使用してコンパイルする場合、ポインターのようにスタックから最適化できますが、表示されることはありません-Ofastを使用してスタック上で、ポインターとは異なり、それらを作成できずvolatile、アドレスを取得できないため)、そうでない場合は、使用しなかったように動作します。registerし、値がスタックに格納されます。
-O0では、もう1つの違いは、gccCとgccC const register++では同じように動作しないことです。gcc Cでは、ブロックスコープがgccで最適化されていないため、のconst registerように動作します。clang Cでは、何も実行せず、ブロックスコープの最適化のみが適用されます。gcc Cでは最適化が適用されますが、ブロックスコープでは最適化がありません。gcc C ++では、ブロックスコープの最適化とブロックスコープの最適化の両方が組み合わされています。registerconstregisterconstregisterconstregisterconst
#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 1)ローカル変数を呼び出し先の保存済みレジスタに格納するようにコンパイラに指示します。この場合は rbx 2)変数のアドレスが取得されない場合は、スタック書き込みを最適化するします。(レジスタを割り当てたり、メモリからロードしたりする代わりに)値をすぐconstに置き換えて、デフォルトの動作としてローカル変数をスタックに書き込むようにコンパイラに指示します。const registerこれらの大胆な最適化の組み合わせです。これはそれが得るのと同じくらいスリムです。
また、gccCおよびC ++では、registerそれ自体で、スタック上の最初のローカルに対して、スタック上にランダムな16バイトのギャップが作成されるようconst registerです。これは。では発生しません。
ただし、-Ofastを使用してコンパイルします。register最適化効果は0です。これは、レジスターに入れるか、すぐに作成できる場合は常にそうなり、できない場合はそうではないためです。constCおよびC ++の負荷を最適化しますが、ファイルスコープのみです。volatileそれでも、値はスタックから保存およびロードされます。
.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