Namespaces
Variants

Function definitions

From cppreference.net

関数定義は、関数本体(宣言と文のシーケンス)を関数名と引数リストに関連付けます。 関数宣言 とは異なり、関数定義はファイルスコープでのみ許可されます(ネストされた関数は存在しません)。

C言語は2つの異なる形式の関数定義をサポートしています:

attr-spec-seq (オプション) specifiers-and-qualifiers parameter-list-declarator function-body (1)
specifiers-and-qualifiers identifier-list-declarator declaration-list function-body (2) (C23まで)

ここで

attr-spec-seq - (C23) 関数に適用される 属性 のオプションのリスト
specifiers-and-qualifiers - 以下の組み合わせ:
parameter-list-declarator - 関数パラメータを指定するために パラメータリスト を使用する関数型の宣言子
identifier-list-declarator - 関数パラメータを指定するために 識別子リスト を使用する関数型の宣言子
declaration-list - identifier-list-declarator 内のすべての識別子を宣言する一連の宣言。これらの宣言では初期化子を使用できず、許可される唯一の 記憶クラス指定子 register です。
function-body - この関数が呼び出されるたびに実行される、宣言と文の波括弧で囲まれたシーケンスである 複合文
1) 新形式(C89)の関数定義。この定義は関数自体を導入するとともに、今後のあらゆる 関数呼び出し式 に対する関数プロトタイプとして機能し、引数式から宣言されたパラメータ型への変換を強制します。
int max(int a, int b)
{
    return a>b?a:b;
}
double g(void)
{
    return 0.1;
}
2) (C23まで) 旧式(K&R)関数定義。この定義はプロトタイプとして動作せず、今後の 関数呼び出し式 ではデフォルトの引数プロモーションが実行されます。
int max(a, b)
int a, b;
{
    return a>b?a:b;
}
double g()
{
    return 0.1;
}

目次

説明

関数宣言と同様に、 関数宣言 の場合と同様に、関数の戻り値の型は、 specifiers-and-qualifiers 内の型指定子によって決定され、必要に応じて declarator によって、 宣言 で通常行われるように修飾されることがあります。この戻り値の型は、完全な非配列オブジェクト型または void 型でなければなりません。戻り値の型がcvr修飾される場合、関数型を構築する目的で、その非修飾バージョンに調整されます。

void f(char *s) { puts(s); } // 戻り値の型はvoid
int sum(int a, int b) { return a+b; } // 戻り値の型はint
int (*foo(const void *p))[3] { // 戻り値の型は3つのintの配列へのポインタ
    return malloc(sizeof(int[3]));
}

関数宣言と同様に、関数型を構築する目的で、パラメータの型は関数からポインタへ、配列からポインタへと調整され、互換性のある関数型を決定する目的では、すべてのパラメータ型のトップレベルのcvr修飾子は無視されます。

関数宣言 とは異なり、無名の仮引数は許可されていません(さもなければ、旧式(K&R)の関数定義で競合が発生します)。関数内で使用されない場合でも、仮引数には名前を付ける必要があります。唯一の例外は、特別な仮引数リスト ( void ) です。

(C23まで)

旧式(K&R)の関数定義が削除されたため、関数定義では仮引数を無名にすることができます。無名の仮引数は、関数本体内で名前でアクセスすることはできません。

(C23以降)
int f(int, int); // 宣言
// int f(int, int) { return 7; } // C23までエラー、C23以降OK
int f(int a, int b) { return 7; } // 定義
int g(void) { return 8; } // OK: voidはパラメータを宣言しない

関数本体内部では、すべての名前付きパラメータは lvalue 式であり、それらは自動 storage duration block scope を持ちます。メモリ内でのパラメータのレイアウト(またはメモリに格納されるかどうか)は未規定です:これは calling convention の一部です。

int main(int ac, char **av)
{
    ac = 2; // パラメータは左辺値
    av = (char *[]){"abc", "def", NULL};
    f(ac, av);
}

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

__func__

すべての 関数本体 内で、特別な事前定義変数 __func__ がブロックスコープと静的ストレージ期間を持って利用可能です。これはあたかも開きブレースの直後に以下のように定義されているかのようです:

static const char __func__[] = "function name";

この特別な識別子は、時には 事前定義マクロ定数 __FILE__ および __LINE__ と組み合わせて使用されます。例えば、 assert によって使用されます。

(C99以降)

注記

引数リストは明示的に宣言子内に存在しなければならず、typedefから継承することはできません

typedef int p(int q, int r); // pは関数型 int(int, int) です
p f { return q + r; } // エラー

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

さらに、旧形式の定義では、 declaration-list 内のすべてのパラメータに対する宣言は必須ではなかった。宣言が欠落しているパラメータはすべて int 型を持つ。

max(a, b) // a and b have type int, return type is int
{
    return a>b?a:b;
}
(C99まで)

不具合報告

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

DR Applied to Behavior as published Correct behavior
DR 423 C89 戻り値型が修飾される可能性がある 戻り値型は暗黙的に修飾が解除される

参考文献

  • C17規格 (ISO/IEC 9899:2018):
  • 6.9.1 関数定義 (p: 113-115)
  • C11規格 (ISO/IEC 9899:2011):
  • 6.9.1 関数定義 (p: 156-158)
  • C99規格 (ISO/IEC 9899:1999):
  • 6.9.1 関数定義 (p: 141-143)
  • C89/C90標準 (ISO/IEC 9899:1990):
  • 3.7.1 関数定義

関連項目

C++ documentation for Function definition
日本語訳:
C++ documentation Function definition
変更点: - "for" を "の" に翻訳 - HTMLタグ、属性、C++用語("C++ documentation"、"Function definition")はそのまま保持 - 書式と構造は完全に維持