Namespaces
Variants

Function declarations

From cppreference.net

関数宣言は、関数を指定する 識別子 を導入し、オプションで関数パラメータの型( プロトタイプ )を指定します。関数宣言は( 定義 とは異なり)ファイルスコープだけでなくブロックスコープでも現れます。

目次

構文

関数宣言の 宣言文法 において、 type-specifier シーケンス(宣言子によって修飾される可能性がある)は 戻り値の型 (配列型または関数型以外の任意の型)を指定し、 declarator は以下の 3つ (C23以前) 2つ (C23以降) の形式のいずれかを取ります:

noptr-declarator ( parameter-list ) attr-spec-seq (オプション) (1)
noptr-declarator ( identifier-list ) attr-spec-seq (オプション) (2) (C23まで)
noptr-declarator ( ) attr-spec-seq (オプション) (3)

ここで

noptr-declarator - 非ポインタ宣言子 declarator で、括弧で囲まれていないポインタ宣言子を除く任意の宣言子。この宣言子に含まれる識別子が関数指示子となる識別子である。
parameter-list - 単一のキーワード void または、カンマ区切りの パラメータ のリストのいずれか。リストは 可変長引数パラメータ で終了してもよい。
identifier-list - 識別子のカンマ区切りリスト。この宣言子が旧式の 関数定義 の一部として使用される場合にのみ可能。
attr-spec-seq - (C23) オプションの 属性 リスト。関数型に適用される。
1) 新形式(C89)の関数宣言。この宣言は関数指示子自体を導入するとともに、将来のあらゆる 関数呼び出し式 に対する関数プロトタイプとしても機能し、引数式から宣言されたパラメータ型への変換を強制し、引数の数に対するコンパイル時チェックを行います。
int max(int a, int b); // declaration
int n = max(12.01, 3.14); // OK, conversion from double to int
2) (C23まで) 旧式(K&R)関数定義。この宣言はプロトタイプを導入せず、将来の 関数呼び出し式 はデフォルトの引数プロモーションを実行し、引数の数がパラメータの数と一致しない場合、未定義動作を引き起こします。
int max(a, b)
    int a, b; // definition expects ints; the second call is undefined
{
    return a > b ? a : b;
}
int n = max(true, (char)'a'); // calls max with two int args (after promotions)
int n = max(12.01f, 3.14); // calls max with two double args (after promotions)
3) プロトタイプ宣言ではない関数宣言。この宣言はプロトタイプを導入しません (C23まで) . 新しいスタイルの関数宣言で、 parameter-list void と同等です (C23以降) .

説明

関数の戻り値の型は、 specifiers-and-qualifiers 内の型指定子によって決定され、通常の 宣言 と同様に declarator によって修飾される可能性があります。この型は、非配列オブジェクト型または void 型でなければなりません。関数宣言が定義でない場合、戻り値の型は 不完全型 でも構いません。戻り値の型はcvr修飾できません:修飾された戻り値の型は、関数型を構築する目的で非修飾版に調整されます。

void f(char *s);                    // 戻り値の型はvoid
int sum(int a, int b);              // sumの戻り値の型はint
int (*foo(const void *p))[3];       // 戻り値の型は3つのintの配列へのポインタ
double const bar(void);             // double(void)型の関数を宣言
double (*barp)(void) = bar;         // OK: barpはdouble(void)へのポインタ
double const (*barpc)(void) = barp; // OK: barpcもdouble(void)へのポインタ

関数宣言子は、型指定子と修飾子を共有できる限り、他の宣言子と組み合わせることができます

int f(void), *fip(), (*pfi)(), *ap[3]; // 2つの関数と2つのオブジェクトを宣言
inline int g(int), n; // エラー: inline修飾子は関数専用です
typedef int array_t[3];
array_t a, h(); // エラー: 配列型は関数の戻り値の型として使用できません

関数宣言が任意の関数の外側に現れる場合、それが導入する識別子は ファイルスコープ および 外部リンケージ を持ちます。ただし、 static が使用されている場合、または以前のstatic宣言が可視である場合は除きます。宣言が他の関数内で発生する場合、識別子はブロックスコープ(および内部または外部リンケージのいずれか)を持ちます。

int main(void)
{
    int f(int); // 外部リンケージ、ブロックスコープ
    f(1); // 定義はプログラム内のどこかで利用可能である必要があります
}

宣言内のパラメータ 関数定義 の一部ではないもの (C23まで) は名前を付ける必要がありません:

int f(int, int); // 宣言
// int f(int, int) { return 7; } // エラー: 定義ではパラメータ名を指定する必要があります
// この定義はC23以降で許可されています

各パラメータは parameter-list 内の declaration であり、以下の追加プロパティを持つ単一の変数を導入します:

  • 宣言子内の識別子はオプションです (ただし、この関数宣言が関数定義の一部である場合を除く) (until C23)
int f(int, double); // OK
int g(int a, double b); // こちらもOK
// int f(int, double) { return 1; } // エラー: 定義ではパラメータ名を指定する必要があります
// この定義はC23以降で許可されています
int f(static int x); // エラー
int f(int [static 10]); // OK (配列インデックスのstaticは記憶クラス指定子ではない)
  • 配列型のパラメータは対応するポインタ型に調整される (配列宣言子の角括弧間に修飾子がある場合は、修飾されたポインタ型になる場合がある) (C99以降)
int f(int[]); // int f(int*) を宣言
int g(const int[10]); // int g(const int*) を宣言
int h(int[const volatile]); // int h(int * const volatile) を宣言
int x(int[*]); // int x(int*) を宣言
  • 関数型のパラメータは、対応するポインタ型に調整されます
int f(char g(double)); // int f(char (*g)(double)) を宣言
int h(int(void)); // int h(int (*)(void)) を宣言
  • パラメータリストは , ... で終了するか、あるいは ... (C23以降) となる場合があります。詳細は variadic functions を参照してください。
int f(int, ...);
  • パラメータは void 型を持つことはできません(ただしvoidへのポインタ型を持つことは可能です)。完全にキーワード void のみで構成される特別なパラメータリストは、パラメータを取らない関数を宣言するために使用されます。
int f(void); // 正常
int g(void x); // エラー
  • パラメータリスト内でtypedef名またはパラメータ名として扱われる可能性のある識別子は、typedef名として扱われます: int f ( size_t , uintptr_t ) は、型 size_t uintptr_t の無名パラメータを2つ取る関数の新しいスタイルの宣言子として解析され、" size_t "と" uintptr_t "という名前の2つのパラメータを取る関数の定義を開始する古いスタイルの宣言子としては解析されません。
  • パラメータは不完全型を持つことができます またVLA表記 [ * ] (C99以降) を使用できます(ただし、 関数定義 内では、配列からポインタへの変換および関数からポインタへの変換後のパラメータ型は完全型でなければなりません)。

属性指定子シーケンス は関数パラメータにも適用できます。

(C23以降)

関数呼び出しの仕組みに関する詳細は function call operator を、関数からの戻り値については return を参照してください。

注記

C++とは異なり、宣言子 f ( ) f ( void ) は異なる意味を持ちます:宣言子 f ( void ) は引数を取らない関数を宣言する新しい形式(プロトタイプ)の宣言子です。宣言子 f ( ) は(関数定義で使用される場合を除き) 不定 の数の引数を取る関数を宣言する宣言子です。

int f(void); // declaration: takes no parameters
int g(); // declaration: takes unknown parameters
int main(void) {
    f(1); // compile-time error
    g(2); // undefined behavior
}
int f(void) { return 1; } // actual definition
int g(a,b,c,d) int a,b,c,d; { return 2; } // actual definition
(C23まで)

関数 定義 とは異なり、パラメータリストはtypedefから継承されることがあります

typedef int p(int q, int r); // pは関数型 int(int, int) です
p f; // int f(int, int) を宣言します

C89では、 specifiers-and-qualifiers は省略可能であり、省略された場合、関数の戻り値の型はデフォルトで int となる( declarator によって修正される可能性あり)。

*f() { // int*を返す関数
   return NULL;
}
(C99まで)

不具合報告

以下の動作変更に関する欠陥報告書は、以前に公開されたC標準に対して遡及的に適用されました。

DR 適用対象 公開時の動作 正しい動作
DR 423 C89 戻り値型が修飾される可能性がある 戻り値型は暗黙的に修飾が解除される

参考文献

  • C23規格 (ISO/IEC 9899:2024):
  • 6.7.7.4 関数宣言子(プロトタイプを含む) (p: 130-132)
  • C17規格 (ISO/IEC 9899:2018):
  • 6.7.6.3 関数宣言子(プロトタイプを含む)(p: 96-98)
  • C11規格 (ISO/IEC 9899:2011):
  • 6.7.6.3 関数宣言子(プロトタイプを含む)(p: 133-136)
  • C99規格 (ISO/IEC 9899:1999):
  • 6.7.5.3 関数宣言子(プロトタイプを含む)(p: 118-121)
  • C89/C90標準 (ISO/IEC 9899:1990):
  • 3.5.4.3 関数宣言子(プロトタイプを含む)

関連項目

C++ ドキュメント for 関数宣言