型修飾子

型修飾子は、2 つのうちいずれかの特性を識別子に割り当てます。 const 型修飾子は、変更不可能なオブジェクトを宣言します。 volatile 型修飾子は、同時実行されるスレッドなど、そのプログラムの制御範囲以外の要素によって値を変更できる項目を宣言します。

型修飾子 constrestrictvolatile は、宣言内で 1 回だけ使用できます。 型修飾子は型指定子との併用が可能ですが、複数項目宣言で、最初のコンマの後にこれらを指定することはできません。 たとえば、次の宣言は有効です。

typedef volatile int VI;
const int ci;

次の宣言は無効です。

typedef int *i, volatile *vi;
float f, const cf;

型修飾子が意味を持つのは、式の左辺値として識別子にアクセスする場合のみです。 左辺値および式の詳細については、「左辺値と右辺値の式」を参照してください。

構文

type-qualifier:
const
restrict
volatile

const および volatile

有効な constvolatile の宣言を次に示します。

int const *p_ci;      // Pointer to constant int
int const (*p_ci);   // Pointer to constant int
int *const cp_i;     // Constant pointer to int
int (*const cp_i);   // Constant pointer to int
int volatile vint;     // Volatile integer

配列型の指定に型修飾子が含まれる場合、要素は修飾されますが、配列型は修飾されません。 関数型の指定に修飾子が含まれる場合、その動作は未定義になります。 volatileconst はいずれも、オブジェクトの値の範囲または算術的特性に影響を与えません。

  • const キーワードを使用できるのは、基本型、集約型、任意の型のオブジェクトへのポインター、または typedef を変更する場合です。 const 型修飾子のみを使用して項目を宣言した場合、その型は const int と見なされます。const 変数は初期化したり、ストレージの読み取り専用領域に配置したりできます。 const へのポインターを宣言する場合に const キーワードを使用すると便利です。この方法でポインターを宣言すると、関数がそのポインターを一切変更できなくなります。

  • プログラムのどの時点でも、未知のプロセスが volatile 変数にアクセスし、その値を使用または変更できると見なされます。 コマンド ラインで指定した最適化にかかわらず、 volatile 変数に値を割り当てたり参照したりするコードが必ず (それが何も影響しない場合でも) 生成されます。

volatile を単独で使用した場合、int を指定したと見なされます。 volatile 型指定子を使用すれば、特別なメモリ位置へ確実にアクセスできます。 シグナル ハンドラー、同時実行のプログラム、または特殊なハードウェア (メモリ マップト I/O 制御レジスタなど) がアクセスまたは変更できるデータ オブジェクトで volatile を使用します。 変数が有効な間だけ volatile として宣言するか、または 1 つの参照を volatile にキャストできます。

  • 項目は constvolatile の両方であってもかまいません。その場合、自身のプログラムではその項目を変更できませんが、非同期のプロセスでは変更できます。

restrict

C99 で導入され、/std:c11 または /std:c17 モードで利用可能な restrict 型修飾子は、ポインター宣言に適用できます。 ポインターが指し示しているものではなく、ポインターが修飾されます。

restrict は、現在のスコープ内の他のポインターによって同じメモリ位置が参照されていないことを示す、コンパイラへの最適化ヒントです。 つまり、そのポインターまたはそれから派生した値 (ポインター + 1 など) のみが、ポインターの有効期間中にオブジェクトにアクセスするために使用されます。 これは、コンパイラでより最適化されたコードを生成するのに役立ちます。 C++ には同等のメカニズムとして __restrict があります

restrict は、ユーザーとコンパイラの間の取り決めであることに注意してください。 restrict でマークされたポインターのエイリアスを作成した場合、結果は未定義になります。

restrict を使用する例を次に示します。

void test(int* restrict first, int* restrict second, int* val)
{
    *first += *val;
    *second += *val;
}

int main()
{
    int i = 1, j = 2, k = 3;
    test(&i, &j, &k);

    return 0;
}

// Marking union members restrict tells the compiler that
// only z.x or z.y will be accessed in any scope, which allows
// the compiler to optimize access to the members.
union z 
{
    int* restrict x;
    double* restrict y;
};

関連項目

/std (言語の標準バージョンの指定)
宣言と型