掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板2

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.25338

関数ポインタについて
投稿者---いなほ(2006/01/14 17:17:06)


いろいろ参考書を見たりしていますが関数ポインタが理解できません。
どのように勉強すれば理解できるのでしょうか?
漠然としていますが、よろしくお願いします。


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:関数ポインタについて 25340 Blue 2006/01/14 17:23:29
<子記事> Re:関数ポインタについて 25392 kz3 2006/01/15 17:28:08


No.25340

Re:関数ポインタについて
投稿者---Blue(2006/01/14 17:23:29)


具体的にあなたがわからないところを ここ で質問してみればどうでしょうか?
(自分では〜だけど、〜となって上手くいかない とか具体的に)
どの参考書よりも受け答えが出来るため理解しやすいと思いますよ。


この投稿にコメントする

削除パスワード

No.25344

Re:関数ポインタについて
投稿者---いなほ(2006/01/14 18:00:25)


ご指摘ありがとうございます。
例文が以下のようにあるのですが、
普通に関数を呼び出すのと変わりがないように思います。
どのように理解すればよいのでしょうか?

void test1(){
    int a,b,c;
    a = 10;
    b = 1;
    c = a * b;
    printf("%d\n",c);
}
void test2(){
    int a,b,c;
    a = 10;
    b = 2;
    c = a * b;
    printf("%d\n",c);
}

//メイン
void main(void){
    //関数ポインタのセット
    void (*func[3])()={test1,test2};

    //モードの設定
    int mode = 2;

    (*func[mode])();

}



この投稿にコメントする

削除パスワード

No.25346

Re:関数ポインタについて
投稿者---επιστημη(2006/01/14 18:36:39)


>普通に関数を呼び出すのと変わりがないように思います。
>どのように理解すればよいのでしょうか?

//モードの設定
int mode = 2;
(*func[mode])();

モードが300通りあったら、ずらずらと

 switch ( mode ) {
 case 0: test0(); break;
 case 1: test1(); break;
 … 延々と300行
 }

って、やりたかないでしょ?



この投稿にコメントする

削除パスワード

No.25347

Re:関数ポインタについて
投稿者---Blue(2006/01/14 18:41:30)


>    int mode = 2;
>    (*func[mode])();
とりあえず、これは間違っていますね。

>    void (*func[3])()={test1,test2};
より
func[ 0 ] = test1;
func[ 1 ] = test2;
func[ 2 ] = NULL

なわけなんで、
func[ 2 ] を使おうとしているのでダメですね。



この投稿にコメントする

削除パスワード

No.25359

Re:関数ポインタについて
投稿者---いなほ(2006/01/15 00:08:58)


ご回答ありがとうございます。
確かにmodeが増えると面倒になることが理解できました。
そこで質問ですが、
void func[]={test1,test2,test3};
これをグローバル宣言して同じようにmainで呼び出すには
どのようにすれば良いのでしょうか?
よろしくお願いします。


この投稿にコメントする

削除パスワード

No.25360

Re:関数ポインタについて
投稿者---επιστημη(2006/01/15 00:18:44)


#include <stdio.h>

typedef void (*func_type)();

void en() { puts("Hello"); }
void ja() { puts("こんにちは"); }
void de() { puts("Guten tag"); }

func_type func[] = { en, ja, de };

int main() {
  int i;
  for ( i = 0; i < 3; ++i ) {
    (*func[i])();
  }
  return 0;
}




この投稿にコメントする

削除パスワード

No.25392

Re:関数ポインタについて
投稿者---kz3(2006/01/15 17:28:08)


>いろいろ参考書を見たりしていますが関数ポインタが理解できません。
>どのように勉強すれば理解できるのでしょうか?
>漠然としていますが、よろしくお願いします。

関数へのポインタをテーブルに入れて添え字によって呼び出す関数を
切り替えたり出来ます。

というのは既にεπιστημηさんが答えていますが他にも用途があります。

まずは以下の3パターン、6個のファイルを用意します。

パターン1
/* main1.c */ #include <stdio.h> int main( void ) { func1(); return 0; } /* func1.c */ #include <stdio.h> /* 外部に公開している関数 */ void func1( void ) { printf("func1() called.\n"); }
パターン2
/* main2.c */ #include <stdio.h> int main( void ) { func2(); return 0; } /* func2.c */ #include <stdio.h> /* 外部に公開していない関数 */ static void func2( void ) { printf("func2() called.\n"); }
パターン3
/* main3.c */ #include <stdio.h> /* 外部から参照可能な関数ポインタ(インターフェース) */ void ( *func )( void ); int main( void ) { func_init(); /* 外部の関数を呼び出す */ func(); /* 関数ポインタを通して関数を呼び出す */ return 0; } /* func3.c */ #include <stdio.h> /* 外部が公開しているインターフェース */ extern void ( *func )( void ); /* 外部に公開しない関数 */ static void func3( void ) { printf("func3() called.\n"); } /* 外部に公開している関数 */ void func_init( void ) { /* インターフェースを通して外部に関数ポインタを渡す */ func = func3; }


1ファイル1モジュールという単位で中と外を区別します。

それぞれ同じ番号同士のファイルをコンパイル・リンクします。

パターン1のfunc1()は外部に公開している関数なのでmain()から呼び出せるのでコンパイルできます。
( main.c関数がコンパイルされる時点ではfunc1()の名前を知りません。
名前を解決するのはリンカの役割です。)

パターン2のfunc2()は外部に非公開なのでmain()から呼び出せないのでコンパイルできません。

パターン3のfunc3()も外部に非公開なのでこのままでは呼び出せません。
ですが外部にfunc3()を呼び出すための窓口を設けています。
それが外部に公開しているfunc_init()で、この関数はmain()から呼び出せます。
またmain3.cでは外部との窓口となる関数ポインタfuncを外部に公開しています。
func_init()はインターフェースを通してfunc3()のポインタをfuncに渡します。

こうすることでインターフェースを通して本来非公開のfunc3()を呼び出すことができます。


この投稿にコメントする

削除パスワード

No.25400

Re:関数ポインタについて
投稿者---いなほ(2006/01/15 19:35:21)


皆さまありがとうございます。
パターン3で質問があります。

>パターン3のfunc3()も外部に非公開なのでこのままでは呼び出せません。
>ですが外部にfunc3()を呼び出すための窓口を設けています。
>それが外部に公開しているfunc_init()で、この関数はmain()から呼び出せます。
上記ですがmain3.cのソースには
extern void func_init( void );
のプロトタイプ宣言は必要になりますよね?

>またmain3.cでは外部との窓口となる関数ポインタfuncを外部に公開しています。
上記は
void ( *func )( void );
これを指していると思いますが、どうしてこのように宣言するのでしょうか?
func_init()関数が戻り値、引数なしだからvoid (*変数名)(void)としているのでしょうか?

何度も聞きすみませんが、
よろしくお願いします。


この投稿にコメントする

削除パスワード

No.25415

Re:関数ポインタについて
投稿者---kz3(2006/01/16 10:32:29)


>>パターン3のfunc3()も外部に非公開なのでこのままでは呼び出せません。
>>ですが外部にfunc3()を呼び出すための窓口を設けています。
>>それが外部に公開しているfunc_init()で、この関数はmain()から呼び出せます。
>上記ですがmain3.cのソースには
>extern void func_init( void );
>のプロトタイプ宣言は必要になりますよね?

プロトタイプ宣言なしでコンパイルできませんでしたか?

プロトタイプ宣言というのはコンパイラに関数の戻り値と引数の情報を教えてあげて、
関数呼び出しにおける文法のチェックができるようにするためのものです。

例えば
void FUNC( void ){} int main( void ){ FUNC(); return 0; }
とあった時、 main()関数よりも先にFUNC()関数の定義がありコンパイラはFUNC()関数の 戻り値と引数の情報を覚えます。 でmain()関数内でFUNC()関数を呼び出そうとすると先ほど覚えた戻り値と引数の情報を使って 関数呼び出しが正しい文法で行われているかをチェックします。 逆に
int main( void ){ FUNC(); return 0; } void FUNC( void ){}
とあると、 main()関数内でFUNC()関数が呼ぼうとした時にまだFUNC()は定義されていないので コンパイラはFUNC()関数の戻り値と引数を知りません。 そこでコンパイラは戻り値の型をデフォルト(int型?)と仮定して処理を進めます。 でFUNC()関数の定義をチェックするときになって初めてFUNC()関数の 戻り値の型と引数の情報を知らされるわけです。 この時、仮定と実際と異なれば「関数宣言の型の不一致」というエラーになります。 それじゃ、関数を定義する順番は関数が呼び出される順番を考慮して定義しなきゃいけないのか? っていうことで導入されたのがプロトタイプ宣言(だそう)です。 # 私もコレ、何かの本で読みました。 関数を定義する前にファイル先頭(一般的にはヘッダファイルに書いてインクルード) でそのファイル内で定義される関数の戻り値、関数名、引数の型・数を宣言して コンパイラに関数の情報を教えます。 こうすることで呼び出し順に関係なくコンパイラは関数呼び出しのチェックができるというわけです。 ただしここでのチェックは単一ファイル内に限ります。 でパターン3について話を戻すとfunc_init()関数はmain3.c内では 定義されていないので「int型の戻り値で引数はvoid型だ」と仮定して main3.obj、func3.objを生成します。 この段階ではmain3.c側のfunc_init()関数とfunc3.c側のfunc_init()関数の 型情報が違います。 これを解決するのがリンクの段階でリンカが行います。 # どう名前を解決するのか、というところは残念ながら知りません。−−; # 補足していただける方いましたらよろしくお願いします。 # スレッドタイトルと話変わるから別にいいか・・・ というわけでプロトタイプ宣言ですが今回は"例"なので省きました。 >>またmain3.cでは外部との窓口となる関数ポインタfuncを外部に公開しています。 >上記は >void ( *func )( void ); >これを指していると思いますが、どうしてこのように宣言するのでしょうか? >func_init()関数が戻り値、引数なしだからvoid (*変数名)(void)としているのでしょうか? これはfunc3()関数の戻り値がvoid型の引数なしなのでこのように宣言しました。 ここで何故「func_init()関数」だからと思われたのか、想像するに 「だってfunc3()関数はstatic void型として定義されているじゃないか?」 ということでしょうか。 残念ですがstaticは型名ではなくて記憶クラス名です。 # 記憶クラスについては掲示板2で説明しています。 例えば
/* main3.c */ static void( *func )( void ); /* func3.c */ extern void( *func )( void ); static void func3( void ){ /* 省略 */ } void func_init( void ) { func = func3; }
このようにmain3.c内の関数ポインタにstaticをつけてしまうと 外部に公開しない変数になってしまい、func3.cから参照できなくなってしまいます。 # まぁこれもポインタを渡せば参照できます。 # ってことで練習問題。(無視して構いません^^;)
/* main3.c */ static void( *func )( void ); int main( void ) { func_init( どうしたらfunc_init()関数に関数ポインタをセットしてもらえるか ); /* ちゃんとfunc3()関数へのポインタが渡っていれば画面に表示される */ func(); return 0; } /* func3.c */ /* extern void( *func )( void ); 使うのをやめる */ static void func3( void ) { /* 省略 */ } void func_init( 仮引数をどう変えればmain3.cの関数ポインタを受け取れるか ) { func3()の関数ポインタ(アドレス)をどうすればmain3.cの関数ポインタに渡せるか }
ヒント:int型へのポインタへのポインタは・・・




この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧