Namespaces
Variants

Declarations

From cppreference.net

declaration は、1つまたは複数の identifiers をプログラムに導入し、それらの意味とプロパティを指定するC言語の構成要素です。

宣言は任意のスコープで現れます。各宣言はセミコロンで終了し( と同様)、以下の 2つの (C23まで) 3つの (C23以降) 異なる部分で構成されます:

specifiers-and-qualifiers declarators-and-initializers  (任意) ; (1)
attr-spec-seq specifiers-and-qualifiers declarators-and-initializers ; (2) (C23以降)
attr-spec-seq ; (3) (C23以降)

ここで

specifiers-and-qualifiers - 任意の順序での空白区切りのリスト:
  • 型指定子:


declarators-and-initializers - declarators のカンマ区切りリスト(各宣言子は追加の型情報や宣言する識別子を提供)。宣言子には 初期化子 を付随させることが可能。 enum struct union 宣言では declarators を省略可能で、その場合は列挙定数やタグのみを導入する。
attr-spec-seq - (C23) オプションの 属性 リスト。宣言されたエンティティに適用されるか、単独で現れた場合は属性宣言を形成する。
1,2) 単純宣言。オブジェクト、関数、struct/union/enumタグ、typedef、または列挙定数を示す1つ以上の識別子を導入します。
3) 属性宣言。いかなる識別子も宣言せず、その意味が標準で規定されていない場合、実装定義の意味を持つ。

例えば、

int a, *b=NULL; // "int"は型指定子、
                // "a"は宣言子
                // "*b"は宣言子でNULLはその初期化子
const int *f(void); // "int"は型指定子
                    // "const"は型修飾子
                    // "*f(void)"は宣言子
enum COLOR {RED, GREEN, BLUE} c; // "enum COLOR {RED, GREEN, BLUE}"は型指定子
                                 // "c"は宣言子

宣言で導入される各識別子の型は、 型指定子 によって指定される型と、その 宣言子 によって適用される型修飾の組み合わせによって決定されます。 auto 指定子が使用された場合、変数の型は推論される可能性もあります。 (C23以降)

Attributes (C23以降) specifiers-and-qualifiers 内に現れることがあり、その場合、先行する指定子によって決定される型に適用されます。

目次

翻訳の説明: - 「Contents」を「目次」に翻訳しました - C++専門用語(Declarators、Definitions、Redeclaration)は原文のまま保持しました - HTMLタグ、属性、数値は一切変更していません - フォーマットと構造は完全に保持されています

宣言子

各宣言子は以下のいずれかです:

identifier attr-spec-seq  (optional) (1)
( declarator ) (2)
* attr-spec-seq  (optional) qualifiers  (optional) declarator (3)
noptr-declarator [ static (optional) qualifiers  (optional) expression ]

noptr-declarator [ qualifiers  (optional) * ]

(4)
noptr-declarator ( parameters-or-identifiers ) (5)
1) この宣言子が導入する識別子。
2) 任意の宣言子は括弧で囲むことができます。これは配列へのポインタや関数へのポインタを導入するために必要です。
3) pointer declarator : 宣言 S * cvr D ; は D S によって決定される型への cvr 修飾ポインタとして宣言する。
4) 配列宣言子 : 宣言 S D [ N ] D S によって決定される型の N 個のオブジェクトの配列として宣言する。 noptr-declarator は、括弧で囲まれていないポインタ宣言子を除く他のあらゆる宣言子である。
5) 関数宣言子 : 宣言 S D ( params ) D をパラメータ params を受け取り S を返す関数として宣言する。 noptr-declarator は、括弧で囲まれていないポインタ宣言子を除く他のあらゆる宣言子である。

この構文の背景にある考え方は、宣言子によって宣言された識別子が、宣言子と同じ形式の式内に現れる場合、その識別子は型指定子シーケンスによって指定された型を持つことになるというものです。

struct C
{
    int member; // "int" は型指定子
                // "member" は宣言子
} obj, *pObj = &obj;
// "struct C { int member; }" は型指定子
// 宣言子 "obj" は struct C 型のオブジェクトを定義
// 宣言子 "*pObj" は C へのポインタを宣言
// 初期化子 "= &obj" はそのポインタの初期値を提供
int a = 1, *p = NULL, f(void), (*pf)(double);
// 型指定子は "int"
// 宣言子 "a" は int 型のオブジェクトを定義
//   初期化子 "=1" はその初期値を提供
// 宣言子 "*p" は int へのポインタ型のオブジェクトを定義
//   初期化子 "=NULL" はその初期値を提供
// 宣言子 "f(void)" は void を受け取り int を返す関数を宣言
// 宣言子 "(*pf)(double)" は double を受け取り int を返す関数への
//   ポインタ型のオブジェクトを定義
int (*(*foo)(double))[3] = NULL;
// 型指定子は int
// 1. 宣言子 "(*(*foo)(double))[3]" は配列宣言子:
//    宣言される型は "/ネストされた宣言子/ 3つの int の配列"
// 2. ネストされた宣言子は "*(*foo)(double))" で、ポインタ宣言子
//    宣言される型は "/ネストされた宣言子/ 3つの int の配列へのポインタ"
// 3. ネストされた宣言子は "(*foo)(double)" で、関数宣言子
//    宣言される型は "/ネストされた宣言子/ double を受け取り
//        3つの int の配列へのポインタを返す関数"
// 4. ネストされた宣言子は "(*foo)" で(関数宣言子の構文で必要とされる
//        括弧で囲まれた)ポインタ宣言子
//    宣言される型は "/ネストされた宣言子/ double を受け取り
//        3つの int の配列へのポインタを返す関数へのポインタ"
// 5. ネストされた宣言子は "foo" で、識別子
// この宣言は識別子 "foo" を "double を受け取り 3つの int の配列への
//    ポインタを返す関数へのポインタ" 型のオブジェクトとして導入
// 初期化子 "= NULL" はこのポインタの初期値を提供
// もし "foo" が宣言子の形式の式で使用された場合、その型は int となる
int x = (*(*foo)(1.2))[0];

他の宣言子の一部ではない各宣言子の終端は、 シーケンスポイント です。

すべての場合において、 attr-spec-seq attributes のオプションのシーケンスです (C23以降) 。識別子の直後に現れる場合、それは宣言されているオブジェクトまたは関数に適用されます。

定義

definition は、宣言する識別子に関するすべての情報を提供する宣言です。

enum または typedef のすべての宣言は定義です。

関数に関して、関数本体を含む宣言は function definition です:

int foo(double); // 宣言
int foo(double x) { return x; } // 定義

オブジェクトの場合、ストレージを割り当てる宣言( 自動または静的 、ただしexternを除く)は定義であり、ストレージを割り当てない宣言( 外部宣言 )は定義ではありません。

extern int n; // 宣言
int n = 10; // 定義

struct および union の場合、メンバーのリストを指定する宣言は定義となります:

struct X; // 宣言
struct X { int n; }; // 定義

再宣言

同じスコープ内で同じ識別子に対する別の宣言が先に現れる場合、宣言は識別子を導入できません。ただし、以下の場合は例外です: scope

  • オブジェクトの宣言は リンケージ (外部または内部)を持つものは繰り返すことができます:
extern int x;
int x = 10; // 正常
extern int x; // 正常
static int n;
static int n = 10; // 正常
static int n; // 正常
  • Non-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つの構造体/共用体/列挙体タグを宣言するか、または少なくとも1つの列挙定数を導入する必要があります。

宣言子の一部が 可変長配列 (VLA) 宣言子である場合、宣言子全体の型は「可変修飾型」として知られています。可変修飾型から定義される型もまた可変修飾型 (VM) です。

任意の可変修飾型の宣言は ブロックスコープ または関数プロトタイプスコープでのみ現れ、構造体や共用体のメンバーになることはできません。VLAは自動または割り当てられた 記憶域期間 しか持てませんが、VLAへのポインタなどのVM型は静的であってもよいです。VM型の使用には他の制限もあります、 goto switch longjmp を参照してください。

(C99以降)

static_assert はC文法の観点からは宣言と見なされる(宣言が現れる場所であればどこにでも現れる可能性がある)が、識別子を導入せず、宣言構文にも従わない。

(C11以降)

属性 宣言も宣言と見なされます(したがって、宣言が現れる場所であればどこにでも現れる可能性があります)が、識別子を導入しません。 ; 単体で attr-spec-seq がないものは属性宣言ではなく、文です。

(C23以降)

参考文献

  • C23規格 (ISO/IEC 9899:2024):
  • 6.7 宣言 (p: 未定)
  • C17規格 (ISO/IEC 9899:2018):
  • 6.7 宣言 (p: 78-105)
  • C11規格 (ISO/IEC 9899:2011):
  • 6.7 宣言 (p: 108-145)
  • C99標準 (ISO/IEC 9899:1999):
  • 6.7 宣言 (p: 97-130)
  • C89/C90標準 (ISO/IEC 9899:1990):
  • 3.5 宣言

関連項目