C ++でキーワードを登録する


89

違いは何ですか

int x=7;

そして

register int x=7;

私はC ++を使用しています。


8
@GMan:ANSI Cでは、レジスタオブジェクトのアドレスを取得できません。この制限はC ++には適用されません
Brian R. Bondy

1
@ブライアン:うーん、その通りです。これは今メモにありますが(アドレスが取得された場合はおそらく無視されます)、必須ではありません。知っておくと良い。(まあ、ある種。:P)
GManNickG 2010

8
再開するための投票registerは、CとC ++の間で異なるセマンティクスを持っています。
CBベイリー

3
この結果、Cでは、配列レジスタを作成することにより、配列からポインタへの変換を禁止するregister int a[1];ことができます。その宣言では、その配列にインデックスを付けることはできません。試してみると、UB
Johannes Schaub – litb 2010

2
確かに、私は再開することに投票しました。違いがあることを知る前に、私は閉じることに投票しました。
GManNickG 2010

回答:


24

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;


22
最後のビットの例が役立つ場合があります。
Dennis Zickefoose 2010

14
2011年以降、この回答は正しくありません。キーワードautoを単純に省略することはできません...おそらく回答を更新できます。
ウォルター

2
@ウォルター:何が変わったのか引用できますか?私はすべての言語の変更に従わなかった。
スーパーキャット2014

2
@supercat、はい、現時点でregisterはありますが、非推奨であり、C ++ 17用に削除する提案があります。
Jonathan Wakely 2014年

3
en.cppreference.com/w/cpp/language/autoによると、C ++ 11以降autoは自動型推定に使用されるようになりましたが、それ以前は、変数を「自動的に」保存することを指定するために使用されていました(したがって、キーワード(「プロセッサのレジスタ」を意味する)とは対照的に、スタック上にあると思いますregister
ギヨーム

97

register はコンパイラへのヒントであり、その変数をメモリではなく(たとえば、スタックではなく)プロセッサレジスタに格納するようにアドバイスします。

コンパイラは、そのヒントに従う場合と従わない場合があります。

「そうではないキーワード(または、別の名前によるコメント)」のハーブサッターによると:

レジスタ指定子は、自動指定子と同じセマンティクスを持っています。


2
ただし、C ++ 17以降、非推奨、未使用、予約済みです。
ZachB

@ZachB、これは正しくありません。レジスタは、C ++ 17に予約されているが、それはまだ動作しますCのレジスタとほぼ同じと機能。
ルイスケルシー

@LewisKelsey未使用で、C ++ 17仕様で予約されています。これはstorage-class-specifier文法の1つではなく、定義されたセマンティクスもありません。準拠するコンパイラは、Clangのようにエラーをスローする可能性があります。それでも、一部の実装ではそれが許可され、無視されるか(MSVC、ICC)、最適化のヒントとして使用されます(GCC)。open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.htmlを参照してください。しかし、私はある点でミスピークをしました:それはC ++ 11で非推奨になりました。
zachB


26

今日のコンパイラでは、おそらく何もありません。もともとは、アクセスを高速化するために変数をレジスターに配置するためのヒントでしたが、今日のほとんどのコンパイラーはそのヒントを無視して、自分で決定します。


9

ほぼ確実に何もありません。

register 使用する予定のコンパイラへのヒントです x多くするであり、レジスターに配置する必要があると考える。

ただし、コンパイラーは、平均的な(またはエキスパートでさえ)プログラマーよりもレジスターに配置する値を決定するのにはるかに優れているため、コンパイラーはキーワードを無視して、必要なことを実行します。



7

registerキーワードが有用でした:

  • インラインアセンブリ。
  • エキスパートC / C ++プログラミング。
  • キャッシュ可能な変数の宣言。

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では使用および予約されていません。


2
また、「register」キーワードは、スレッドやマルチタスクのない単一のC ++プログラムを実行しているマイクロコントローラーでのみ役立つことを付け加えておきます。'register'変数が特別なCPUレジスタから移動されないようにするには、C ++プログラムがCPU全体を所有している必要があります。
Santiago Villafuerte

@SantiagoVillafuerte回答を編集して追加しますか?
ncomputers 2016年

私は自分の答えがよくわかりません...もっともらしいように聞こえますが。他の人がそれを承認または不承認にするために、コメントとして残したいと思います。
Santiago Villafuerte 2016年

1
@SantiagoVillafuerteこれは実際には当てはまりません。マルチタスクシステムでは、アプリケーションではなくOSのコンテキスト切り替えがレジスタの保存/復元を担当する場合です。すべてのCPU命令の後でコンテキストを切り替えるわけではないので、レジスターに入れることは絶対に意味があります。ここでの他の答え(コンパイラはレジスタ割り当てに関してあなたの意見を単に気にしないということ)はより正確です。
キュービック

示した例は、実際にはGCCの明示的レジスタ変数拡張機能を使用しています。これはregisterストレージクラス指定子とは異なり、GCCでも引き続きサポートされています。
zachB

1

コンパイラのオプティマイザに2つの変数があり、1つをスタックに強制的にスピルする場合を考えてみます。たまたま、両方の変数がコンパイラに対して同じ重みを持っています。違いがない場合、コンパイラーは変数の1つを任意にこぼします。一方、registerキーワードは、どの変数がより頻繁にアクセスされるかについてのヒントをコンパイラーに提供します。これはx86プリフェッチ命令に似ていますが、コンパイラオプティマイザ用です。

明らかに、registerヒントはユーザーが提供する分岐確率ヒントに似ており、これらの確率ヒントから推測できます。コンパイラーは、ある分岐が頻繁に行われることを知っている場合、分岐関連の変数をレジスターに保持します。ですから、ブランチのヒントをもっと気にかけ、を忘れることをお勧めしregisterます。理想的には、プロファイラーはコンパイラーと何らかの形で通信し、そのようなニュアンスについて考えることさえしないようにする必要があります。


1

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.