web-dev-qa-db-ja.com

registerキーワードが作成されたのはなぜですか?

キーワードではない(または別の名前のコメント) byHerb Sutter

そうです、一部のキーワードは、意味的には空白、栄光に満ちたコメントと同等です。

そして

C++言語がキーワードを予約語として扱う理由を見てきました。また、C++プログラムにセマンティックな違いをもたらさない2つのキーワード(autoとregister)を見てきました。それらを使用しないでください。とにかくそれらは単なる空白であり、空白を入力するより速い方法があります。

auto(C++ 11ではないかもしれません)やregisterのようなキーワードに値がない場合、なぜそれらが作成され使用されたのですか?

変数の前にregisterを含めても違いがない場合

#include<stdio.h>
int main(){
    register int a = 15;
    printf("%d\n%d\n",&a,a);
    return 0;
}

上記のプログラムでエラーが発生するのはなぜですか?

test_register.c:関数「main」内:

test_register.c:4:2:エラー:レジスタ変数「a」のアドレスが要求されました

printf( "%d\n%d\n"、&a、a);

次のプログラムはC++で動作します。

#include<iostream>
int main(){
    register int a = 15;
    std::cout<<&a<<'\n'<<a;
    return 0;
}
44
ayushgp

登録

Cでは、registerストレージクラスがコンパイラへのヒントとして使用され、 変数はレジスタに優先的に格納される必要がある であることを表現しました。 register変数を実際のレジスタに保存するヒントは尊重される場合とされない場合がありますが、どちらの場合でも関連する制限が適用されることに注意してください。 C11、6.7.1p6(強調鉱山)を参照してください:

ストレージクラス指定子registerを使用したオブジェクトの識別子の宣言は、オブジェクトへのアクセスが可能な限り高速であることを示唆しています。 そのような提案が効果的である範囲は実装定義です。[脚注121]

[脚注121]実装は、任意のregister宣言を単にauto宣言として扱うことができます。 ただし、アドレス可能なストレージが実際に使用されているかどうかにかかわらず、ストレージクラス指定子registerで宣言されたオブジェクトの任意の部分のアドレスは計算できません、明示的に(6.5.3.2で説明した単項&演算子を使用)または暗黙的に(6.3.2.1で説明したように配列名をポインターに変換して)。したがって、ストレージクラス指定子registerで宣言された配列に適用できる演算子は、sizeof_Alignof

C++では、単に未使用の予約キーワードですが、Cコードとの構文上の互換性のために保持されていると想定するのは合理的です。

オート

Cでは、autoストレージクラスは自動ストレージの変数を定義しますが、 関数ローカル変数はデフォルトでauto なので、通常は使用されません。

同様に、構文上の互換性のためだけに最初にC++に引き継がれたと仮定するのは合理的です。ただし、後で独自の意味を持ちます( type inference )。

Cのregisterは2つの目的を果たしました。

  • パフォーマンスのために変数をレジスタに保存する必要があるというコンパイラーへのヒント。現在、この使用方法はほとんど使用されていません。
  • プログラマーがレジスターに格納されないように変数を使用しないようにします。この使用は、やや時代遅れのIMOに過ぎません。

これはconstに似ています。

  • 変数が読み取り専用メモリに格納される可能性があるというコンパイラーへのヒント。
  • プログラマーが変数に書き込むのを防ぎます

例として、この単純な関数を考えてみましょう:

_int sum(const int *values, size_t length) {
    register int acc = 0;
    for (size_t i = 0; i < length; ++i) {
        acc += values[i];
    }
    return acc;
}
_

プログラマーはregisterを記述して、アキュムレーターをスタックから切り離し、更新されるたびにメモリーが書き込まれるのを防ぎます。実装が次のように変更された場合:

_// Defined in some other translation unit
void add(int *dest, int src);

int sum(const int *values, size_t length) {
    register int acc = 0;
    for (size_t i = 0; i < length; ++i) {
        add(&acc, values[i]);
    }
    return acc;
}
_

acc変数は、アドレスがadd()呼び出しに使用されたときにレジスタに格納できなくなりました。これは、レジスタにアドレスがないためです。したがって、コンパイラーは_&acc_をエラーとしてフラグ付けし、accがレジスターに存在しないようにしてコードのパフォーマンスを破壊した可能性があることを知らせます。

これは、コンパイラーが無力で、関数全体で変数が1か所に存在する初期の頃にはずっと重要でした。今日では、変数はそのアドレスの取得時に一時的にスタックに移動されるだけで、その寿命の大部分をレジスタで費やすことができます。つまり、次のコード:

_/* Passed by reference for some reason. */
void debug(const int *value);

int sum(const int *values, size_t length) {
    int acc = 0;
    for (size_t i = 0; i < length; ++i) {
        acc += values[i];
    }
    debug(&acc);
    return acc;
}
_

古いコンパイラでは、accが関数全体のスタック上に存在することになります。最新のコンパイラは、debug()呼び出しの直前までaccをレジスタに保持します。

最近のCコードでは、通常registerキーワードを使用しません。

25
Tavian Barnes

C99の根拠は、キーワードregisterのコンテキストをさらに提供します。

国際標準の根拠—プログラミング言語— C

§6.7.1ストレージクラス指定子

register変数のアドレスを取得できないため、ストレージクラスregisterのオブジェクトは、他のオブジェクトとは異なるスペースに効果的に存在します。 (関数はさらに3番目のアドレス空間を占有します。)これにより、レジスタを宣言する通常の理由である最適な配置の候補になります。しかし、より積極的な最適化の候補にもなります。

10
Yu Hao