宣言
宣言 (declaration) は、1つ以上の識別子をプログラムに導入し、その意味とプロパティを指定するC言語の構文です。
宣言はどのスコープにも現れることができます。各宣言は(文と同様に)セミコロンで終わり、2つ(C23まで)3つ(C23以降)の異なる部分から構成されます。
指定子および修飾子 宣言子および初期化子(任意) ; |
(1) | ||||||||
属性指定子シーケンス 指定子および修飾子 宣言子および初期化子 ; |
(2) | (C23以降) | |||||||
属性指定子シーケンス ; |
(3) | (C23以降) | |||||||
ここで、
| 指定子および修飾子 | - | 空白で区切られたリストで、順序は任意です。
|
| 宣言子および初期化子 | - | カンマ区切りの宣言子のリスト(各宣言子は追加の型情報や宣言する識別子を提供します)。宣言子には初期化子を伴うことができます。enum、struct、union宣言は宣言子を省略でき、その場合は列挙定数やタグのみを導入します。 |
| attr-spec-seq | - | (C23) 宣言されたエンティティに適用される属性の任意のリスト。単独で現れる場合は属性宣言を形成します。 |
例えば、
int a, *b=NULL; // "int" is the type specifier, // "a" is a declarator // "*b" is a declarator and NULL is its initializer const int *f(void); // "int" is the type specifier // "const" is the type qualifier // "*f(void)" is the declarator enum COLOR {RED, GREEN, BLUE} c; // "enum COLOR {RED, GREEN, BLUE}" is the type specifier // "c" is the declarator
宣言で導入される各識別子の型は、型指定子によって指定された型と、その宣言子によって適用される型の変更の組み合わせによって決定されます。auto指定子が使われた場合、変数の型は推論されることもあります。(C23以降)
属性(C23以降)は指定子および修飾子の中に現れることがあり、その場合は先行する指定子によって決定された型に適用されます。
目次 |
[編集] 宣言子
各宣言子は、以下のいずれかです。
| 識別子 属性指定子シーケンス(任意) | (1) | ||||||||
( 宣言子 ) |
(2) | ||||||||
* 属性指定子シーケンス(任意) 修飾子(任意) 宣言子 |
(3) | ||||||||
noptr-declarator [ static(任意) 修飾子(任意) 式 ]noptr-declarator |
(4) | ||||||||
noptr-declarator ( パラメータまたは識別子 ) |
(5) | ||||||||
DをSによって決定される型のオブジェクトN個からなる配列として宣言します。noptr-declaratorは、括弧で囲まれていないポインタ宣言子以外の任意の宣言子です。Dをパラメータparamsを取りSを返す関数として宣言します。noptr-declaratorは、括弧で囲まれていないポインタ宣言子以外の任意の宣言子です。この構文の背後にある論理は、宣言子によって宣言された識別子が、宣言子と同じ形式の式に現れたときに、型指定子シーケンスによって指定された型を持つということです。
struct C { int member; // "int" is the type specifier // "member" is the declarator } obj, *pObj = &obj; // "struct C { int member; }" is the type specifier // declarator "obj" defines an object of type struct C // declarator "*pObj" declares a pointer to C, // initializer "= &obj" provides the initial value for that pointer int a = 1, *p = NULL, f(void), (*pf)(double); // the type specifier is "int" // declarator "a" defines an object of type int // initializer "=1" provides its initial value // declarator "*p" defines an object of type pointer to int // initializer "=NULL" provides its initial value // declarator "f(void)" declares a function taking void and returning int // declarator "(*pf)(double)" defines an object of type pointer // to function taking double and returning int int (*(*foo)(double))[3] = NULL; // the type specifier is int // 1. declarator "(*(*foo)(double))[3]" is an array declarator: // the type declared is "/nested declarator/ array of 3 int" // 2. the nested declarator is "*(*foo)(double))", which is a pointer declarator // the type declared is "/nested declarator/ pointer to array of 3 int" // 3. the nested declarator is "(*foo)(double)", which is a function declarator // the type declared is "/nested declarator/ function taking double and returning // pointer to array of 3 int" // 4. the nested declarator is "(*foo)" which is a (parenthesized, as required by // function declarator syntax) pointer declarator. // the type declared is "/nested declarator/ pointer to function taking double // and returning pointer to array of 3 int" // 5. the nested declarator is "foo", which is an identifier. // The declaration introduces the identifier "foo" to refer to an object of type // "pointer to function taking double and returning pointer to array of 3 int" // The initializer "= NULL" provides the initial value of this pointer. // If "foo" is used in an expression of the form of the declarator, its type would be // int. int x = (*(*foo)(1.2))[0];
他の宣言子の一部でないすべての宣言子の終わりは、シーケンスポイントです。
すべての場合において、attr-spec-seqは属性(C23以降)の任意のシーケンスです。識別子の直後に現れる場合、それは宣言されているオブジェクトまたは関数に適用されます。
[編集] 定義
定義 (definition) は、宣言する識別子に関するすべての情報を提供する宣言です。
関数については、関数本体を含む宣言が関数定義です。
int foo(double); // declaration int foo(double x) { return x; } // definition
オブジェクトについては、ストレージを割り当てる宣言(自動または静的、ただしexternは除く)が定義であり、ストレージを割り当てない宣言(外部宣言)は定義ではありません。
extern int n; // declaration int n = 10; // definition
structおよびunionについては、メンバのリストを指定する宣言が定義です。
struct X; // declaration struct X { int n; }; // definition
[編集] 再宣言
同じスコープ内の同じ識別子に対する別の宣言が先行している場合、宣言でその識別子を導入することはできません。ただし、以下の場合を除きます。
- リンケージを持つ(外部または内部)オブジェクトの宣言は繰り返すことができます。
extern int x; int x = 10; // OK extern int x; // OK static int n; static int n = 10; // OK static int n; // OK
- 非VLAのtypedefは、同じ型を命名している限り繰り返すことができます。
typedef int int_t; typedef int int_t; // OK
struct X; struct X { int n; }; struct X;
これらの規則は、ヘッダファイルの使用を単純化します。
[編集] 備考
|
C89では、任意の複合文(ブロックスコープ)内の宣言は、ブロックの先頭、どの文よりも前に現れなければなりません。 また、C89では、intを返す関数は関数呼び出し演算子によって暗黙的に宣言されることがあり、旧式の関数定義を使用する場合、int型の関数パラメータを宣言する必要はありませんでした。 |
(C99まで) |
空の宣言子は禁止されています。単純な宣言は、少なくとも1つの宣言子を持つか、少なくとも1つのstruct/union/enumタグを宣言するか、少なくとも1つの列挙定数を導入しなければなりません。
|
宣言子の一部が可変長配列(VLA)宣言子である場合、宣言子全体の型は「可変変更型 (variably-modified type)」として知られています。可変変更型から定義された型もまた可変変更型(VM)です。 可変変更型の宣言は、ブロックスコープまたは関数プロトタイプスコープにのみ現れることができ、structやunionのメンバになることはできません。VLAは自動または割り当てられた記憶域期間しか持てませんが、VLAへのポインタのようなVM型は静的(static)にすることができます。VM型の使用には他にも制限があります。 goto、switch、longjmpを参照してください。 |
(C99以降) |
|
static_assertは、C文法の観点からは宣言と見なされます(そのため、宣言が現れることができる場所ならどこにでも現れることができます)。しかし、それらは識別子を導入せず、宣言の構文にも従いません。 |
(C11 以降) |
|
属性宣言もまた宣言と見なされます(そのため、宣言が現れることができる場所ならどこにでも現れることができます)。しかし、それらは識別子を導入しません。attr-spec-seqなしの単一の |
(C23以降) |
[編集] 参照
- C23標準 (ISO/IEC 9899:2024)
- 6.7 Declarations (p: TBD)
- C17標準 (ISO/IEC 9899:2018)
- 6.7 Declarations (p: 78-105)
- C11標準 (ISO/IEC 9899:2011)
- 6.7 Declarations (p: 108-145)
- C99標準 (ISO/IEC 9899:1999)
- 6.7 Declarations (p: 97-130)
- C89/C90標準 (ISO/IEC 9899:1990)
- 3.5 Declarations
[編集] 関連項目
| C++ documentation for Declarations
|