- 締切済み
C言語の基本的な質問ですが、関数へのポインタの宣言
関数へのポインタの質問です。 下のように、関数へのポインタを使ったプログラムを書きました。 (関数へのポインタを理解するためのものなので、実用的な意味はありません。(*^_^*) また、このプログラムはコンパイルもリンクも実行も問題なく出来ます。) #include <stdio.h> int add_func(int,int); (*func_p0) (int,int); int main(void) { int (*func_p1) (int,int); int (*func_p2) ( ); int hoge0,hoge1,hoge2; func_p0=add_func; hoge0=func_p0(3,5); printf("0 : 3+5は%d\n",hoge0); func_p1=add_func; hoge1=func_p1(3,5); printf("1 : 3+5は%d\n",hoge1); func_p2=add_func; hoge2=func_p2(3,5); printf("2 : 3+5は%d\n",hoge2); return(0); } int add_func(int x, int y) { return(x+y); } func_p0のように戻り値の型を書かない場合と、func_p1やfunc_p2のように戻り値の型を書くのとでは何が違うのでしょうか。 func_p0は外部変数ですが、自動変数にする(main関数の中で同様に宣言。)とコンパイルエラーになります。 それはなぜですか。 func_p1のように引数の型が書いてあるのと、func_p2のように引数の型が書いていないのでは何が違うのでしょうか。 int (*func_p2) ( );というのは、int (*func_p2) (void);とは違うんですよね?
- みんなの回答 (1)
- 専門家の回答
みんなの回答
- a-kuma
- ベストアンサー率50% (1122/2211)
> func_p0のように戻り値の型を書かない場合と、func_p1やfunc_p2のように戻り値の型を書くのとでは何が違うのでしょうか。 見た目が違います(いや、冗談ではなく)。 Cの場合、関数の宣言で、戻り値を省略した場合には int を返す関数だと解釈する、と 規格で決められています。 また、外部変数の場合には、型を省略すると int である、と解釈します(古いスタイル なので、警告が出るはず)。 あと、次の質問にあるように、記述できる場所が変わってきたりする、という副作用が あるようですね。 > func_p0は外部変数ですが、自動変数にする(main関数の中で同様に宣言。)とコンパイルエラーになります。 > それはなぜですか。 それは、コンパイラによって挙動(エラーにするかどうか)が変わってくるかもしれません。 コンパイラは、関数の中に入ると、変数の宣言か実行文の記述があると解釈してゆくのですが、 変数の宣言は「型」として有効なシンボルであること、実行文は「予め宣言されたシンボル」 であることが、求められます。 > func_p1のように引数の型が書いてあるのと、func_p2のように引数の型が書いていないのでは何が違うのでしょうか。 関数の宣言で引数が省略された場合には、可変個の引数である、と解釈されます(これも 規格で定められています)。「可変個の引数」というのは、例えば、printf() の二番目などです。 int (*func_p2)(); は、 int (*func_p2)(...); と等価です。質問に書かれている 通り、int (*func_p2)(void); とは別ものです。 因みに、c++ の場合には、引数を省略した場合には、void と解釈されます。
補足
>Cの場合、関数の宣言で、戻り値を省略した場合には int を返す関数だと解釈する、と規格で決められています。 手元にある「新ANSI C言語辞典」という本によると、 関数宣言には、関数原型を宣言する場合と、関数へのポインタを定義する場合があるそうですね。 今、質問しているのは、関数へのポインタの件なので、 質問で挙げた例で言うと (*func_p0) (int,int); というのは、 int (*func_p0) (int,int); と同じなのだ、 ということがおっしゃりたいのですね。 >また、外部変数の場合には、型を省略すると int である、と解釈します(古いスタイルなので、警告が出るはず)。 すみません。こっちは上とどう違うのかよくわかりません。例を挙げてください。 変数宣言なしに変数を使うと、それをint型の変数とみなす、という意味ですか? (ちなみに質問で挙げたプログラムは、1つの警告も出ません。) #そういうことではないのかな。あまり深い意味はないのでしょうか。 残念ながら、func_p0をmain関数の中で宣言するとエラーになる理由はわかりませんでした。 もっとも、 intを返す関数へのポインタを定義するときは、必ずintを書いておけば問題ないわけですね。 >int (*func_p2)(); は、 int (*func_p2)(...); と等価です。質問に書かれている通り、int (*func_p2)(void); とは別ものです。 int (*func_p2) ( );のように引数の型を書かないときは、引数の型がどのような関数でも指すことが出来る。 だから、int add_func(int,int); というのも指すことができる。(←これは質問のプログラム中に既出) いうまでもなく、下のプログラムのように、int ret9_func(void); も指せる。 また、標準関数のprintfは int printf( const char * , ... ); です。そういう関数を指すこともできる。 ですから、下のように、 func_p2=printf; という代入も全く問題ないわけですね。(実際、問題なく動作した。) #include <stdio.h> int ret9_func(void); int main(void) { int (*func_p2) ( ); func_p2=ret9_func; /* 引数がvoidの関数を代入 */ printf( "関数func_p2の戻り値は%dです。ret9_funcのことですね。\n\n", func_p2() ); func_p2=printf; /* 引数が可変個の関数printfを代入 */ func_p2("func_p2=printf;をやりました。func_p2はprintfと同じですよね。\n"); return(0); } int ret9_func(void) { return(9); } #実用的な意味にとぼしいですが。;^_^A 以上のことから考えるに、 int (*func_p2) ( ); と定義すれば、どんな「引数」の関数でも指すことができるわけですけど、 「戻り値」の型がどのような関数でも指すことができる、なんてのはできないんですね?