名前空間
変種
操作

関数定義

From cppreference.com
< c‎ | language

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

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

属性指定子シーケンス(オプション) 指定子・修飾子 仮引数リスト宣言子 関数本体 (1)
指定子・修飾子 識別子リスト宣言子 宣言リスト 関数本体 (2) (C23まで)

ここで、

attr-spec-seq - (C23以降)関数に適用される、属性のオプションのリスト
指定子・修飾子 - 以下の組み合わせ
仮引数リスト宣言子 - 関数の仮引数を指定するために仮引数リストを使用する、関数型の宣言子
識別子リスト宣言子 - 関数の仮引数を指定するために識別子リストを使用する、関数型の宣言子
宣言リスト - 識別子リスト宣言子内のすべての識別子を宣言する宣言のシーケンス。これらの宣言は初期化子を使用できず、許可される唯一の記憶域クラス指定子registerです。
関数本体 - 複合文、つまり波括弧で囲まれた宣言と文のシーケンスであり、この関数が呼び出されるたびに実行されます。
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;
}

目次

[編集] 説明

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

void f(char *s) { puts(s); } // return type is void
int sum(int a, int b) { return a+b; } // return type is int
int (*foo(const void *p))[3] { // return type is pointer to array of 3 int
    return malloc(sizeof(int[3]));
}

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

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

(C23まで)

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

(C23以降)
int f(int, int); // declaration
// int f(int, int) { return 7; } // Error until C23, OK since C23
int f(int a, int b) { return 7; } // definition
int g(void) { return 8; } // OK: void doesn't declare a parameter

関数本体内では、名前付きの各仮引数は左辺値式であり、自動記憶域期間ブロックスコープを持ちます。メモリ内での仮引数のレイアウト(またはメモリに格納されるかどうか)は未規定です。これは呼び出し規約の一部です。

int main(int ac, char **av)
{
    ac = 2; // parameters are lvalues
    av = (char *[]){"abc", "def", NULL};
    f(ac, av);
}

関数呼び出しの仕組みに関するその他の詳細については関数呼び出し演算子を、関数からの復帰についてはreturnを参照してください。

__func__

すべての関数本体内で、ブロックスコープと静的記憶域期間を持つ特別な定義済み変数__func__が、開始波括弧の直後で次のように定義されているかのように利用できます。

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

この特別な識別子は、assertなどによって、定義済みマクロ定数__FILE__および__LINE__と組み合わせて使用されることがあります。

(C99以降)

[編集] 注釈

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

typedef int p(int q, int r); // p is a function type int(int, int)
p f { return q + r; } // Error

C89では、指定子・修飾子はオプションであり、省略された場合、関数の戻り値の型は(宣言子によって修正される可能性はありますが)デフォルトでintになりました。

さらに、旧形式の定義では宣言リスト内のすべての仮引数に宣言が必要ではありませんでした。宣言が欠落している仮引数は型intを持ちました。

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

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に発行されたC規格に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
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++ ドキュメント
English 日本語 中文(简体) 中文(繁體)