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

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

このQ&Aに関連する最新のQ&A

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で質問しましょう!

このQ&Aと関連する良く見られている質問

Q「void ( *signal(int sig, void (*func)(int)) ) (int)」の (int)

signal関数の書式についてですが、

  void ( *signal(int sig, void (*func)(int)) ) (int);

最後に付く(int)は一体何でしょうか?
このような関数の書式ははじめて見ました。
UNIX系の何かでしょうか。
回答よろしくお願いします。

Aベストアンサー

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t sighandler);
より後半部分のtypedefを置き換えると
sighandler_t signal(int signum, void (*sighandler)(int));
つぎに戻り値の部分のtypedefを置き換えると
void (*signal(int signum, void (*sighandler)(int)))(int);
となります。
(
sighandler_t signal(int signum, void (*sighandler)(int));
の「signal(int signum, void (*sighandler)(int))」をAと置き換えて
sighandler_t A;
からtypedefを置き換えると
void (*A)(int);
となり、Aを戻すと
void (*signal(int signum, void (*sighandler)(int)))(int);
となる。
)

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t sighandler);
より後半部分のtypedefを置き換えると
sighandler_t signal(int signum, void (*sighandler)(int));
つぎに戻り値の部分のtypedefを置き換えると
void (*signal(int signum, void (*sighandler)(int)))(int);
となります。
(
sighandler_t signal(int signum, void (*sighandler)(int));
の「signal(int signum, void (*sighandler)(int))」をAと置き換えて
sighandler_t A;
からtypedefを置き換...続きを読む

Qfor(s=p; *p; p++)の*p(ポインタ)の意味

for(s=p; *p; p++)の*p(ポインタ)の意味
C言語初心者です。
今ポインタを勉強しているのですが、
for文で上記のようなものが出てきて、意味が分からず困っています。
*pで*p != NULL と同じような意味になるみたいなのですが…。
どうしてそのような意味になるのでしょうか?

ちなみにsとpはポインタで、
sには配列(入力した文字列)の先頭アドレスが入っています。
pは文字列を指していて○○○○○NULL ←になるから上記のような条件で
回るんだろうなぁとはなんとなく考えているのですが。

Aベストアンサー

念のため:
ヌルポインタは「ビットパターンとして」0 じゃないかもしれませんが, ソースプログラムにおいて「ポインタが要求される場面」で「0」とあれば, それは「ヌルポインタ」です.
もうちょっと厳密に書くと「整定数 0」はヌルポインタに変換される.

Qint select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)について

見当違いな質問かもしれませんがお願いします。

複数のソケットを監視する際にselectを使う場合のことですが、
selectの動作と戻り値について疑問があります。

http://www.linux.or.jp/JM/html/LDP_man-pages/man2/select.2.html
ここを参照すると、selectの戻り値は
「更新された 3 つのディスクリプタ集合に含まれているディスクリプタの数 (つまり、 readfds, writefds, exceptfds 中の 1 になっているビットの総数) を返す。」
とあります。
私の中でselectは登録してあるFDのうち、一つでも動きがあれば即座にselectを抜けるもの、という認識です。
この認識だとreadfds,writefdsが引数として与えられているとしても、
どちらかのfd_setのうち、一つでも動きがあればselect文は
抜けてしまうことになります。とすると、戻り値として
「readfds, writefds, exceptfds 中の 1 になっているビットの総数」
は常に1ということになってしまいます。しかし、総数というからには
複数同時に1になることもあるはずです。

私の認識が間違っているとは思うのですが、どう間違っているのかわかりません。
select文の動きについて詳しく教えていただけないでしょうか。
または良いページがあれば教えてください。

見当違いな質問かもしれませんがお願いします。

複数のソケットを監視する際にselectを使う場合のことですが、
selectの動作と戻り値について疑問があります。

http://www.linux.or.jp/JM/html/LDP_man-pages/man2/select.2.html
ここを参照すると、selectの戻り値は
「更新された 3 つのディスクリプタ集合に含まれているディスクリプタの数 (つまり、 readfds, writefds, exceptfds 中の 1 になっているビットの総数) を返す。」
とあります。
私の中でselectは登録してあるFDのうち、一つでも動きが...続きを読む

Aベストアンサー

>私の中でselectは登録してあるFDのうち、一つでも動きがあれば即座にselectを抜けるもの、という認識です。
この認識はあっています。
しかし、selectを呼び出す以前にOKになっているFDがあれば、それらは全てビットがONになります。

話しを簡単にする為に、受信のみのソケットを3つ作成したとします。
これらの3つのソケットに向けて相手が電文を送ったとします。
その状態でまだ、こちらはselectを呼び出さずにいます。しばらくしてから、selectを呼び出すと、selectは即座にリターンし、3つのビットが一度にONになっているはずです。
一方、相手が、一切電文を送ってない状態で、selectを呼び出した場合は、何れかのビットがONになればリターンするので、そのときは、貴方が想像しているように
ビットの総数は1になる可能性が高いです。
従って、相手が電文を送る前にselectを呼び出すか、送った後にselectを呼び出すかは、その時のタイミングにより異なります。従って、ビット数の総和が常に1であるとは、考えない方が無難です。(1つのソケットしか使用しない場合は別ですが・・・)

>私の中でselectは登録してあるFDのうち、一つでも動きがあれば即座にselectを抜けるもの、という認識です。
この認識はあっています。
しかし、selectを呼び出す以前にOKになっているFDがあれば、それらは全てビットがONになります。

話しを簡単にする為に、受信のみのソケットを3つ作成したとします。
これらの3つのソケットに向けて相手が電文を送ったとします。
その状態でまだ、こちらはselectを呼び出さずにいます。しばらくしてから、selectを呼び出すと、selectは即座にリターンし、3つのビ...続きを読む

QSendMessage(hW,WM_CREATE,0,0);を

SendMessage(hW,WM_CREATE,0,0);
を実行するとシステムがWM_DOWNやWM_CHARを発行しなくなるみたいです
というのはそれ以降キー入力を無視するようになるのです
いったんアプリをアイコン化してウィンドウ化するとWM_DOWNやWM_CHARを発行するようになります
WM_CREATEを送ってもWM_DOWNやWM_CHARを発行しなくなるのを阻止するために何か方法はないでしょうか?

Aベストアンサー

>プログラムのイニシャライズのために送ったのですが送らないで住むプログラムに変更しました

普通はそんな方法はとりません。
システムが何をするか分からないからです。

自分でメッセージを定義して、初期化処理を行うようするためのメッセージを送るほうが無難です。
WM_CREATEと同じ処理を初期化処理として行わせたいのであれば、初期化処理を関数化して自分で定義したメッセージでも呼び出せばいいのですし。


>作ったプッシュボタンを押してシステムがWM_COMMANDを送ってきた後キー関係のメッセージを送ってくれなくなります

プッシュボタンがキーボードフォーカスを持ってのるでは?

ボタンがキーボードフォーカスを持っていてもキー関連のメッセージを親ウィンドウが受け取りたいのであれば、サブクラス化をするしかないでしょう。

Qint i,j; \n i=0,j=5;

int i,j;
i=0;
j=5:
と書いてあるソースは普通ですが、
int i,j;
i=0,j=5:
と書いてあるソースもあります。
後者はC++の正しい書式ですか?

カンマ演算子というのは後者のカンマのことですか?

Aベストアンサー

 正しい書式です。

i=0,j=5;
 における、「,」をカンマ演算子といいます。2項の演算子です。カンマで区切られた演算を「左から順番に」実行し、最後の演算を結果として返します。
 したがって、例の文であれば、i=0を実行し、次にj=5を実行。そして、j=5の結果の5を結果として返します。
 ・・・
 が、本来的には、あまり、例のような使い方はしませんね。よく見られるのは、次のような場合です。

 for (i=0,j=0 ; i < 50 ; ++i,++j) {

 のような形でよく見られます。for文の各式は、一つの式でなければならないので、こんな書き方をするわけです。初期化と更新部が一つにまとまり、ループが読みやすくなるのが利点かな。


人気Q&Aランキング

おすすめ情報