都道府県穴埋めゲーム

関数へのポインタの質問です。
下のように、関数へのポインタを使ったプログラムを書きました。
(関数へのポインタを理解するためのものなので、実用的な意味はありません。(*^_^*)
また、このプログラムはコンパイルもリンクも実行も問題なく出来ます。)

#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);とは違うんですよね?

A 回答 (1件)

> 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) ( ); と定義すれば、どんな「引数」の関数でも指すことができるわけですけど、
「戻り値」の型がどのような関数でも指すことができる、なんてのはできないんですね?

補足日時:2002/01/28 22:46
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!