- ベストアンサー
C言語のtypedefの質問
Cビギナーです。 私のプログラムが長くなるのは、 ファイルの分割をしてないからだと知った今日この頃なのですが、 それを勉強するためにあるホームページを見ていたのですが、 次のようなサンプルプログラムがありました。 /* main.c */ #include <stdio.h> extern int func(int); typedef int (*pf)(int); extern pf getaddress(void); int main(void) { printf("%d\n",(getaddress())(4)); return 0; } /* sub.c */ static int func(int c) { return c*10; } typedef int (*pf)(int); pf getaddress(void) { return func; } ここで、typedef int (*pf)(int);の部分が分かりません。 intを(*pf)(int)で置き換えているのでしょうが、 それ自体がどういう意味か分かりません… どなたか教えて下さい。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
>pf getaddress(void) C言語では関数型は返せなくとも、 関数へのポインタ型は戻り値として 指定することが出来ます。 上記で引数無しのint型引数にとるint型を返す関数型への ポインタを返す関数として認識されます。 関数型はそれ自体がポインタとなります。 しかし、プログラム内(関数内)で書くと引数リストを要求される 処理系もあり、コンパイラによっては「return func;」では エラーになります。 関数型にアンパサンド(&)をつけて宣言子型派生を行っても、 取得できるポインタ自体は変わることが無いので、 「return &func; 」とするのが正しいと思います。 上記はあくまでも関数内で特定の関数型指定してを返す場合で、 殆どの場合関数ポインタを使う上では最も多く使われるのが、 関数テーブルだと思います。 関数のポインタ自体を返すというのはあまり行いませんが 以下は今回ののような任意の関数ポインタを返すサンプルです。 /* sub.h */ typedef enum{ TEST1_FUNC, TEST2_FUNC, TEST3_FUNC, MAX_DISPATCH, }fcode; typedef int (*pf)(int); pf getaddress( fcode code ); /* main.c */ #include "sub.h" int main( void ) { pf pfunc; pfunc = getaddress( TEST2_FUNC ); pfunc( 4 ); return 0; } /* sub.c */ #include <stdio.h> int test1( int num ); int test2( int num ); int test3( int num ); static pf ftable[] = { test1, test2, test3, NULL, }; pf getaddress( fcode code ) { if ( TEST1_FUNC > code || MAX_DISPATCH <= code ) { return NULL; } return ftable[code]; } int test1( int num ) { return printf("test1 = %d\n", num); } int test2( int num ) { return printf("test2 = %d\n", num ); } int test3( int num ) { return printf("test3 = %d\n", num ); } ※全角は半角に変換してください
その他の回答 (5)
- Tacosan
- ベストアンサー率23% (3656/15482)
func は「関数指定子」というやつで, 関数指定子が式の中で使われると「特定の場合」を除いて「その関数指定子が指し示す関数へのポインタ」に変換されます. #4 の「&func」のように「&」を付けるのは, この「特定の場合」に相当します (他には sizeof のオペランドになったとき, くらいかな). その場合にはもちろん関数指定子そのものになりますが, 関数指定子を単項 & のオペランドにすると「その関数へのポインタ」に変換されるので, 結局のところ「何もしない」のと同じです. 「return func;」は上の「特定の場合」には当てはまらないので, C の仕様上は勝手に関数へのポインタに変換されるはずです. これが #5 の「そんな処理系は捨てちゃっていいと思う」の理由. その次の「このサンプルプログラムそのものを捨てるべきかもしれない」というのは, はっきり言うと「設計がダメ」だからです. 特に sub.c で「static な関数へのポインタを返している」あたり. 「static な関数」というのは「そのコンパイル単位 (ソースファイル) でのみ使う」ということを言っているようなものなので, 「内部でのみ使う」はずのものを外に漏らしちゃうというのは論理的に間違っています. 当然ではありますが, #4 で示されたコードではそんなことになっていません. まあ, 実害はないけど「func は extern なのか static なのかはっきりしろ」というところからして問題だが....
お礼
詳しい説明ありがとうございます! staticの使い方勉強になりました。 あと、関数指定子もよく分かりました! お説のとおり、 このプログラム使わないことにしました^^; 自分のコードを、これ使って、ファイル分割させようとしたら もう頭の中とコードとごちゃごちゃになって、 aris-wizさんのやり方でリトライしてます。 皆様の回答読んで、 理屈だけでなく、 文面のとおり一応理解して、 とりあえずCに慣れるところからやってみます。 ありがとうございました!
- Tacosan
- ベストアンサー率23% (3656/15482)
とりあえず, このプログラムの return func; をエラーにする処理系は捨てちゃっていいと思う>#3. もっとも, その前にこのサンプルプログラムそのものを捨てるべきかもしれない.
お礼
Tacosan様、どもです。 >をエラーにする処理系は捨てちゃっていいと思う>#3. は了解しました。 Tacosan様はじめ他の回答者の皆様、 ただいま、 皆様方の回答を理解しようと努力中です@@; お礼するの今しばらくお待ちくださいm(__)m
- splwtr
- ベストアンサー率16% (75/461)
解説はANo.1さんの言う通りです。 getaddressの関数定義は間違ってます。 コンパイルエラーになりませんか? また、pfの定義は、1ファイルに纏めて使うといいです。 使い方の例が、分かりにくいので pfを使う例を追加しておきます。 状況によって関数を切り替えたい場合の例です。 ---- a.h typedef int (*pf)(int) ; ---- a.c #include <stdio.h> #include "a.h" typedef struct addr_t { int i_val; pf i_func ; /* 関数ポインタ */ } addr_t; int main... { : addr_t Adr; Adr.i_val = 0; Adr.i_func = func ; /* 状況によって使用する関数をかえる * とりあえず func()を使う */ Adr.i_val = Adr.i_func(4); : } : : :
お礼
splwtr様、ありがとうございます。 私は、C言語はVisual Studio2005でやっているんですが、 エラーになりません^^; 動いてしまいました^^ゞ あ、まだビルドしかやったことないんですが… でも、pfをヘッダーにまとめるというご指摘は 大変参考になりました! あと、実は、スイッチするのがわかっていなくて pfの名前を変えて、たくさん宣言していたのですが、 こうやるといいんですね! 実用上いろいろ参考になるアドバイスありがとうございます!
- koko_u_
- ベストアンサー率18% (459/2509)
>ここで、typedef int (*pf)(int);の部分が分かりません。 関数を指すポインタ型を typedef しています。 そうしないと、その後の getaddress() のプロトタイプ宣言が人には読めない代物になるので。
補足
koko_u様、どもです。 やっぱり、typedef int (*pf)(int);の場合は、 int (*x)(int)な関数のポインターは、 pfであるという意味ぽいのですね。 なんで typedef a b; の時と違う解釈の仕方するのか 不思議でしょうがないのですが、 typedefの理解の仕方として、 そういうルールなんだと、 覚えてしまえばいいのでしょうか?? ご教授いただければ幸いですm(__)m
- asuncion
- ベストアンサー率33% (2127/6289)
> typedef int (*pf)(int); pfは、int型の引数を受取りint型を返す関数へのポインターである、と読みます。
補足
asuncion様、すいません、質問です。 typedef a b; とかですと、 aをbと呼ぶという理解でいいんですよね? ということは、 typedef int (*pf)(int); だと、 intを(*pf)(int)と呼ぶとならないのでしょうか? (この場合、intが(*pf)(int)ってなんやねん、 と私の頭はちんぷんかん状態ですが…) >pfは、int型の引数を受取りint型を返す関数へのポインターである、と読みます。 ですと、 int (*x)(int)をpfと呼ぶというように聞こえて、 こちらの方が理解しやすいのですが… typedefがよく分かっていないのかもしれませんが、 こういう理解でよろしいのでしょうか?@@; ご指導よろしくお願い致します。
お礼
aris-wiz様、回答ありがとうございます。 なんか自分のへぼプログラムでも使えそうなサンプル助かります! 現在、なんか理由は分からないけど、 プログラムは動くんだなーという 非常に結果オーライ的な 気持ちの悪い状況ですが、 まずは慣れよなのかもしれませんね^^;