43章 関数ポインタ

http://www.geocities.jp/ky_webid/c/043.html

関数のポインタとな。

戻り値の型 (*ポインタ名) (引数のリスト);

これが宣言。よく覚えておくように。

関数ポインタは、あくまでもアドレスを保持するだけなので、参照先の関数が別に存在することになります。例えば、次のような関数があったとします。

int myabs(int num);   /* 引数の絶対値を返す関数 */

pという名前の関数ポインタに、myabs関数のアドレスを代入するには、

p = myabs;

とするだけです。次に、関数ポインタから関数を呼び出す方法ですが、これには2通りの方法があります。

ans = (*p)(-100);    /* myabs(-100); という呼び出しと同じ結果 */

また、次のように書くこともできます。

ans = p(-100);       /* myabs(-100); という呼び出しと同じ結果 */

後者の方法では、関数ポインタから参照によって関数を呼んでいるのか、それともp関数という関数を呼んでいるのかが分かりにくくなります。違いをはっきりさせるために、前者の方法を使うことが多いようです。

引用が長くなってしまいました、これはとても重要ですね。確かに単なる関数なのか、ポインタ経由の関数なのか分かりにくいのでちゃんと間接参照する方の書き方の方が良さそうです。

問題1

「配列を使った応用例」で見たサンプルプログラムを変更して、キーボードから、どの演算結果を表示するかを指定できるようにして下さい。

int add(int num1, int num2);
int sub(int num1, int num2);
int mul(int num1, int num2);
int div(int num1, int num2);
int mod(int num1, int num2);

int main(void){
    int (*p[5]) (int num1, int num2) = { add, sub, mul, div, mod };
    int num1, num2, type;
    int i;
    
    puts( "2つの整数を入力して下さい" );
    scanf( "%d %d", &num1, &num2 );
    
    puts("どの演算結果を出力しますか?");
    puts("0: add");
    puts("1: sub");
    puts("2: mul");
    puts("3: div");
    puts("4: mod");
    scanf("%d",&type);
    
    if ( type < 0 || type > 4 ) {
        puts("入力エラー\n");
        return 1;
    }
    
    printf( "%d\n", (*p[type])(num1, num2) );
    return 0;
}

int add(int num1, int num2){
    return ( num1 + num2 );
}

int sub(int num1, int num2){
    return ( num1 - num2 );
}

int mul(int num1, int num2){
    return ( num1 * num2 );
}

int div(int num1, int num2){
    if( num2 == 0 ){ return 0; }
    return ( num1 / num2 );
}

int mod(int num1, int num2){
    if( num2 == 0 ){ return 0; }
    return ( num1 % num2 );
}
$ main
2つの整数を入力して下さい
10 32
どの演算結果を出力しますか?
0: add
1: sub
2: mul
3: div
4: mod
2
320

できました。特に難しいことはないですね。

答え合わせ

解答例では入力処理を繰り返し実行できるようにループさせてますね。問題自体は正解でした。

問題2

構造体のメンバに、2つのint型変数と、関数ポインタを宣言します。また、2つのint型の引数を受け取り、大きい方の値を返すmax関数を作ります。構造体を配列で宣言し、各要素ごとに、2つのint型のうちの大きい方の値を表示するプログラムを作って下さい。

構造体に関数ポインタを突っ込むわけですか。何だか凄いですね。やってみます。

#define MAX (3)

int max (int num1,int num2) {
    if ( num1 > num2 ) {
        return num1;
    }
    return num2;
}

struct s1 {
    int num1;
    int num2;
    int (*max)(int num1,int num2);
};

int main () {
    struct s1 maxnum[MAX];
    int i;
    
    maxnum[0].num1 = 100;
    maxnum[0].num2 = 20;
    
    maxnum[1].num1 = 30;
    maxnum[1].num2 = 99;
    
    maxnum[2].num1 = 70;
    maxnum[2].num2 = 70;
    
    for(i=0;i<MAX;++i){
        maxnum[i].max = max;
        printf("max=%d\n",(*maxnum[i].max)(maxnum[i].num1,maxnum[i].num2));
    }
    
    return 0;
}

一応できました。でも何だか同じ関数のポインタを突っ込んでるだけなので処理的には何の意味も無いような気はしますね。

答え合わせ
/* マクロ定義 */
/* array[index].p から関数を呼び出すマクロ */
#define CALL(index) ( (*array[index].p)(array[index].num1, array[index].num2) )

おお、マクロを定義して呼び出し方を簡略化してますね。こういう使い方もあるわけなんですねー。でもあまりやりすぎるとコードが読みにくくなる気もするのでその辺どうトレードオフするのかが今後の課題です。

問題3

引数に関数ポインタを使用できることを確認するプログラムを考えて作って下さい。

難しそうですがやってみます。

int max (int num1,int num2) {
    if ( num1 > num2 ) {
        return num1;
    }
    return num2;
}

int maxcall (int (*max)(int num1,int num2),int num1,int num2) {
    return (*max)(num1,num2);
}

int main () {
    int num1 = 10, num2 = 99;
    printf("%d",maxcall(max,num1,num2));
}
$ main
99

関数の引数がめちゃ複雑になってしまいましたね。でもちゃんと動いてます。

答え合わせ

あってました。解答例ではabs関数を引数に渡していますね。