関数ポインタを利用して呼び出す関数を動的に変更する



はじめに

状態遷移図をコードに落としたり、オブジェクト指向の多態性(polymorphism)をCのコードで実現したりする場合、呼び出す関数を動的に変更する必要があります。
このページでは、「呼び出す関数を動的に変更する」ような実装を可能にする関数ポインタについて説明します。

関数ポインタとは?

関数ポインタとは、C言語における重要な概念である「ポインタ」の関数版です。
ポインタはあるメモリアドレスを指す「矢印」ですが、関数ポインタは「関数が格納されたメモリアドレスを指す矢印」ということになります。

関数を呼び出す際に、関数ポインタの内容を別の関数が格納されたアドレスに変更することにより、呼び出す関数を任意に変更することが可能です。

関数ポインタのプロトタイプ宣言

関数ポインタのプロトタイプ宣言は下記の通りです。
引数の型や数は、関数ポインタを利用する状況に合わせて変更してください。

関数ポインタのプロトタイプ宣言
typedef int (*FUNCPTR)(int a,int b);

関数ポインタに格納する関数の定義

関数ポインタに格納する関数は、下記のように定義します。
基本的に通常の関数の定義と変わりありませんが、引数はプロトタイプ宣言された関数ポインタと型・数を揃えるようにしてください。

関数ポインタに格納する関数の定義
int multiple(int a,int b)
{
    return(a*b);
}

int divide(int a,int b)
{
    return(a/b);
}

関数ポインタへの関数アドレスの格納

関数ポインタへの関数アドレスの格納は、下記のようにアドレス演算子「&」を用いて行います。

関数ポインタへの関数アドレスの格納
FUNCPTR tFuncPtr;

tFuncPtr=&multiple;

関数ポインタを利用した関数の呼び出し

関数ポインタに格納された関数アドレスを用いて関数を呼び出すには、下記のようにします。

関数ポインタを利用した関数の呼び出し
int result;

result=(tFuncPtr)(a,b);

サンプルコード

以下に関数ポインタを用いた簡単なサンプルコードを示しますので、参考にしてください。

サンプルコード
#include <stdio.h>

typedef int (*FUNCPTR)(int a,int b);

int multiple(int a,int b)
{
    return(a*b);
}

int divide(int a,int b)
{
    return(a/b);
}

void main()
{
    int a,b;
    int result;
    FUNCPTR tFuncPtr;

    a=8;
    b=4;

    tFuncPtr=&multiple;
    result=(tFuncPtr)(a,b);
    printf("Answer is %d\n",result);

    tFuncPtr=&divide;
    result=(tFuncPtr)(a,b);
    printf("Answer is %d\n",result);
}


あなたの探し物は見つかりましたか?
まさにこれだ 参考になった ちょっと違う これじゃない
何かメッセージがあればお願いします

このメッセージを非公開にする

ご注意

・頂いたメッセージは管理者のチェックの後、公開されます。
・メッセージの公開を希望されない場合には、「このメッセージを非公開にする」にチェックを入れてください。
・管理者が不適切と判断したメッセージは公開しませんので、予めご了承ください。


まさにこれだ
41 (52%)
参考になった
21 (27%)
ちょっと違う
8 (10%)
これじゃない
9 (11%)

【これじゃない】 説明不足過ぎる。この管理人はでいだぶるだな (2014/06/25 Wed 00:01:30)

【参考になった】 typedefの型名はなぜそこなのか??? (2014/06/03 Tue 22:37:50)

【まさにこれだ】 簡単に試せた (2013/11/14 Thu 01:12:18)

【まさにこれだ】 分かりやすかった (2013/04/04 Thu 05:46:51)

【まさにこれだ】 とってもわかり易かったです。ありがとうございます。 (2013/02/22 Fri 06:53:33)

【参考になった】 関数がint *func(int a)の場合は int *(*FUNCPTR)(int a)になると信じて試してみます。 (2013/01/12 Sat 12:45:29)

【まさにこれだ】 すごく分かりやすく書いてあったので参考になりました。 (2012/03/31 Sat 15:42:06)

【まさにこれだ】 簡略で分かりやすかった (2012/02/01 Wed 00:42:56)

【ちょっと違う】 関数ポインタの配列をダイナミックにアロケートする方法を知りたかったのですが。 (2011/08/31 Wed 02:09:44)

【ちょっと違う】 関数ポインタの型をプロトタイプ宣言で typedef する必然性を教えてください。変数の宣言時に直接関数ポインタ型を指定してもいいわけですよね? きっと抽象化する意図なのでしょうが,そうしますと FUNCPTR という名前はどんな関数でも代入できそうで,よい名前ではないようです。 (2011/05/05 Thu 02:41:51)
→ プロトタイプ宣言している意図は、ご指摘の通り抽象化と、都度「int (*FUNCPTR)(int a,int b);」と宣言するのが直感的でないと感じたからです。 ご指摘の通り、プロトタイプ宣言は関数ポインタは引数の取る値によって分ける必要があるので、FUNCPTRとするのは良くないかも知れないですね。

【ちょっと違う】 関数ポインタの引数にポインタって指定できるんですか? (2009/12/15 Tue 06:33:57)
→ もちろんできますよ。


目次に戻る
image