C言語には、代入可能な変数で使う関数ポインタ型の他に、関数型も存在します。
関数型は、関数プロトタイプ宣言における、関数(変数)のための型です。
関数定義出現前に関数を呼び出すとき使用する関数プロトタイプ宣言は、変数宣言と同じく、C言語の**宣言文(declaration)**の一つです。
/* 関数ポインタ型変数宣言文 */
int (* add_p)(int, int);
/* 関数プロトタイプ宣言文 */
int add(int, int);
ちなみに、この関数名のadd
も、C言語のシンタックス上では変数ですが、代入は不可能なものです(セマンティックでのエラー)。
そしてtypedef
文も、宣言文です。関数ポインタ型と関数型のtypedef
宣言は以下のようになります。
/* 関数ポインタ型のtypedef宣言文 */
typedef int (* op_p)(int, int);
/* 関数ポインタ型のtypedef宣言文 */
typedef int op_t(int, int);
typedef
宣言で定義された**typedef
名**を用いて、先程の関数ポインタ型変数宣言文と関数プロトタイプ宣言文を記述することができます。
/* 関数ポインタ型変数宣言文 */
op_p add_p;
/* 関数プロトタイプ宣言文 */
op_t add;
後者のプロトタイプ宣言のためにtypedef
を用いる事例は少なく、その存在を知らない人も多いかもしれません。
また、関数型のtypedef
名を用いることで、関数ポインタ型を導出する事もできます。
/* 関数ポインタ型変数宣言文 */
op_t * add_p;
/* 関数ポインタ型のtypedef宣言文 */
typedef op_t * op_p;
ただし、関数型自体にconst
やvolatile
(、restrict
)といった型修飾子(qualifier)を併用することは仕様上は禁止です。もちろん、そのポインタについては、型修飾子をつけることは可能です。
/* 正しくない変数宣言文 */
const op_t * add_p = 0;
/* 正しい関数ポインタ型変数宣言文 */
op_t * const add_p = 0;
前者は、gccもclangも、コンパイルエラーではなくwarningレベルでした。
(ポインタの対象の型についたconst修飾は、代入では(やはりwarningありで)無視するからだろう)
関数型や関数型変数(プロトタイプ宣言)を、sizeof
で利用することは不正です。
/* 不正 */
size_t ftsize = sizeof (int (int, int));
int add(int, int);
/* 不正 */
size_t fsize = sizeof add;
gccもclangもどちらもwarningを出すにとどまり、実行した時の結果は1になりました。
代入できないので、関数型は、関数の戻り値型やstruct
/union
のメンバーとしては使用できません。
しかし、関数の引数では、関数型の引数を記述できます。
これは、関数の引数の配列型がポインタ型扱いになるのと同様、関数の引数の関数型もポインタ型扱いになるためです。
/* opは関数ポインタ型 */
int apply(int op(int, int), int v) {
return (*op)(v, v);
}
typedef int op_t(int, int);
/* opは関数ポインタ型 */
int apply(op_t op, int v) {
return (*op)(v, v);
}
参考: 警告なしコンパイル&実行可能な、関数型使用のコード例
/* cc -std=c89 -pedantic -Wall -Wextra func-type-example.c */
/* types for function (prototype) */
typedef int op2_t(int, int);
/* function prototype declaration with typedef types */
static op2_t add, sub;
/* types for function pointer */
typedef op2_t * op2_ptr1;
typedef int (* op2_ptr2)(int, int);
/* function type in parameter as function pointer type */
static int apply(op2_t op, int a, int b) {
return (*op)(a, b);
}
extern int main() {
const op2_ptr1 padd = &add;
op2_t * psub = ⊂
return padd(10, apply(psub, 2, 3)); /* echo $? => 9 */
}
/* K&R C style definition */
int add(a, b) int a; int b; {
return a + b;
}
/* ANSI C style definition */
int sub(int a, int b) {
return a - b;
}