HOWTO · C

C の register キーワード

宣言、アドレスの制限、ポインター、スコープなど、C 言語の register ストレージクラス指定子がどのように機能するかを学びます。

このページの内容

それをサポートする C のバージョンでは、register はストレージクラス指定子であり、伝統的に、オブジェクトが高速なアクセスの恩恵を受ける可能性があることを示唆するために使われてきました。CPU レジスタへの格納を保証するものでも、プログラムを高速化するものでもありません。コンパイラーがオブジェクトをどのように実装するかを決定し、以下で説明する規則は、コンパイラーが対象とする C 標準に依存する場合があります。

C 言語の register キーワードの概要

register 指定子は、古い最適化モデルを反映しています。プログラマーは、頻繁にアクセスされるオブジェクトをレジスタ格納の候補としてマークできました。現代のコンパイラーはプログラムとターゲットプラットフォームから最適化の判断を行うため、この指定子を加えることは性能の約束にはなりません。対象の C バージョンがそれをサポートし、その言語規則が宣言に適合する場合にのみ使用してください。性能が目的であれば、別途測定してください。

CPU には限られた数のレジスタしかありませんが、C は通常のオブジェクトを各操作の前にレジスタに読み込むことを要求しません。コンパイラーは、プログラムを翻訳し最適化する際に、値をレジスタ、メモリ、またはその他の場所に保持できます。ソースコードは特定の物理的な場所に依存できません。選択した言語モードでコンパイラーが register が廃止されているかサポートされていないと警告する場合は、そのコンパイラーのドキュメントと、プロジェクトに設定された C のバージョンを確認してください。

C 言語での register キーワードの使用

次の完全なプログラムは、3つのローカルオブジェクトに register を使用し、それらの値を出力します。実行可能な例ですが、その出力はここには示しません。

#include <stdio.h>

int main(void) {
    register char letter = 'S';
    register double amount = 10.0;
    register int count = 8;

    printf("letter: %c\n", letter);
    printf("amount: %.1f\n", amount);
    printf("count: %d\n", count);
    return 0;
}

宣言の構文はそれ以外は馴染みのあるものです。register を型と識別子の前に、宣言指定子の中に置きます。この例では固定値を使用しているため、宣言とフォーマットされた出力に集中できます。コンパイラーが歴史的な格納の示唆に従うかどうかは実装の判断であり、プログラムの正しさはその選択に依存してはなりません。

C 言語で register 変数のアドレスを取得する

register ストレージクラス指定子を定義する C のバージョンでは、アドレス演算子 & を register で宣言されたオブジェクトに適用することはできません。この制限は、実装がたまたまオブジェクトの値をメモリに保持している場合でも適用されます。これは言語の制約であり、物理的なレジスタがアドレスを持つかどうかについての記述ではありません。

たとえば、宣言 register int item = 22; は互換性のある言語モードでは有効ですが、int *address = &item; はその制約に違反します。コンパイラーは制約違反を診断しなければなりませんが、その診断の文言、重大度、形式は実装固有です。別のコンパイラーの診断を、保証された出力であるかのようにコピーしないでください。

C のポインター変数で register を使用する

ポインターオブジェクト自体も、それをサポートする C のバージョンでは register を使用できます。ポインターは別のオブジェクトのアドレスを引き続き格納できます。制限の対象は、ポインターオブジェクト自体のアドレスを取得することです。たとえば、int value = 15; の後に register int *cursor = &value; と書くと、cursor は value を指し、*cursor はその指されている整数にアクセスします。

この区別はコードを読むときに役立ちます。&value は通常の value オブジェクトのアドレスを取得しますが、&cursor は register ポインターオブジェクトのアドレスを取得しようとするもので、同じ制約により許可されません。この指定子は、ポインターが何を指すかを変更したり、どちらのオブジェクトがどこに格納されるかを保証したりするものではありません。コンパイラーが宣言を拒否したり、指定子がサポートされていないと報告したりする場合は、対象の言語モードを確認してください。

C で register を他のストレージクラス指定子と一緒に使用する

register 自体がストレージクラス指定子であるため、同じ宣言で別のストレージクラス指定子と組み合わせることはできません。特に、auto register int value;、static register int value;、extern register int value; のような宣言は、register をサポートする言語モードでは有効な組み合わせではありません。

コンパイラーは無効な組み合わせを診断しますが、そのメッセージは固定された文字列として標準化されていません。有効な宣言については、意図する有効期間と結び付けの規則に合った1つのストレージクラス指定子を選んでください。型(これらの例では int)と修飾子は宣言の別の部分であり、2つのストレージクラス指定子を互換にするものではありません。

C 言語の register 変数のスコープ

この指定子は、それをサポートする C のバージョンではブロックスコープのオブジェクトと関数パラメーターのためのものであり、ファイルスコープのオブジェクトには許可されません。関数パラメーターは、たとえば int total(register int count) のように書くことができます。関数本体の中では、register int count = 22; のようなブロックスコープの宣言も許可される形式です。register int count; のようなファイルスコープの宣言は、適用される制約に違反し、診断が必要です。

したがって「ローカル変数」という説明は不完全です。許可される関数パラメーターの場合を曖昧にするおそれがあります。スコープと格納期間は関連する概念ですが、互いに交換可能なものではありません。register 指定子は、オブジェクトが CPU レジスタに存在することを約束せず、その存在によってオブジェクトが通常のスコープの外側から見えるようになることもありません。古いコードを保守する際は、これらの宣言に頼る前に、プロジェクトで選択された C のバージョンとコンパイラーモードを確認してください。