const 型修飾子
C の型システムでは、個々の型には、それぞれ const 修飾子、 volatile 修飾子、 restrict 修飾子 (オブジェクトへのポインタ型の場合のみ) のうちの1つ、2つ、または3つ全部の組み合わせに対応する、その型の修飾されたバージョンがあります。 このページではそのうちの const 修飾子について説明します。
const 修飾子付きで宣言されたオブジェクトは、コンパイラによって読み込み専用メモリに配置されることがあります。 プログラム内で const オブジェクトのアドレスが取られない場合、そのオブジェクトはまったく格納されないこともあります。
const
の意味論は左辺値式にのみ適用されます。 左辺値を必要としない文脈で const 左辺値式が使用されたときは、その const
修飾子は失われます (volatile 修飾子 (もしあれば) は失われないことに注意してください)。
const 修飾された型のオブジェクトを指定する左辺値式、および少なくともひとつの const 修飾された型を持つ構造体型または共用体型 (再帰的に含まれる集成体または共用体のメンバを含みます) のオブジェクトを指定する左辺値式は、変更可能な左辺値ではありません。 特に、代入可能ではありません。
const int n = 1; // const 型のオブジェクト。 n = 2; // エラー、 n の型は const 修飾されています。 int x = 2; // 修飾されていない型のオブジェクト。 const int* p = &x; *p = 3; // エラー、左辺値 *p の型は const 修飾されています。 struct {int a; const int b; } s1 = {.b=1}, s2 = {.b=2}; s1 = s2; // エラー、 s1 の型は修飾されていませんが、 const なメンバを持っています。
const 修飾された構造体型または共用体型のメンバには、その属する型の修飾が付与されます (.
演算子でアクセスしたときと ->
演算子でアクセスしたとき、どちらも)。
struct s { int i; const int ci; } s; // s.i の型は int で、 s.ci の型は const int です。 const struct s cs; // cs.i の型と cs.ci の型はどちらも const int です。
配列型が (typedef を用いて) const 型修飾子付きで宣言された場合は、その配列型ではなく、その要素型が const 修飾されます。 関数型が (typedef を用いて) const 型修飾子付きで宣言された場合は、動作は未定義です。
typedef int A[2][3]; const A a = {{4, 5, 6}, {7, 8, 9}}; // const int の配列の配列。 int* pi = a[0]; // エラー、 a[0] の型は const int* です。
const 修飾された複合リテラルは独立したオブジェクトを表すとは限りません。 それらは、たまたま同じまたはオーバーラップした表現を持つに至った他の複合リテラルや文字列と、記憶域を共有する場合があります。 const int* p1 = (const int[]){1, 2, 3}; const int* p2 = (const int[]){2, 3, 4}; // p2 と p1+1 は等しいかもしれません。 _Bool b = "foobar" + 3 == (const char[]){"bar"}; // b の値は 1 (true) かもしれません。 |
(C99以上) |
非 const 型へのポインタは、同じまたは互換な型の const 修飾されたバージョンへのポインタに、暗黙に変換できます。 逆変換はキャスト式を用いて行えます。
int* p = 0; const int* cp = p; // OK、修飾子を追加します (int → const int)。 p = cp; // エラー、修飾子を除去 (const int → int) することはできません。 p = (int*)cp; // OK、キャストを使用すれば修飾子を除去できます。
T
へのポインタへのポインタは、 const T
へのポインタへのポインタと、互換でないことに注意してください。 2つの型が互換であるためには、その修飾子が等しくなければなりません。
char *p = 0; const char **cpp = &p; // エラー、 char* と const char* は互換な型ではありません。 char * const *pcp = &p; // OK、修飾子を追加します (char* → char*const)。
const 修飾された型のオブジェクトを変更しようとするあらゆる試みは未定義動作です。
const int n = 1; // const 修飾された型のオブジェクト。 int* p = (int*)&n; *p = 2; // 未定義動作。
関数宣言において、配列型の関数引数を宣言するための角括弧内で、キーワード 以下の2つの宣言は同じです。 void f(double x[const], const double y[const]); void f(double * const x, const double * const y); |
(C99以上) |
目次 |
[編集] キーワード
[編集] ノート
C は C++ から const 修飾子を採用しましたが、 C++ と異なり、 C では const 修飾された式は定数式ではありません。 そのため、 case のラベルとして使用したり、静的およびスレッド記憶域期間のオブジェクトや列挙子を初期化するために使用したり、ビットフィールドのサイズとして使用したりすることはできません。 配列のサイズとして使用した場合は、その配列は VLA になります。