★ポインタ(関数ポインタ配列)★


最後は関数ポインタの配列について説明します。

順を追って説明したいので、少しずつ書いていきます。

まずは、↓のプログラムを作ってみてください。

<sample program 176-01>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void Func1(void);

void Func2(void);

int main(void)
{
    void(*pFunc)(void);

    srand((unsigned int)time(NULL));

    if (rand() % 2) {
        pFunc = Func1;
    }
    else {
        pFunc = Func2;
    }

    pFunc();

    return 0;
}

void Func1(void)
{
    printf("Enter Func1\n");
}

void Func2(void)
{
    printf("Enter Func2\n");
}

<実行結果>

Enter Func2
続行するには何かキーを押してください・・・

以前、関数ポインタは同じ引数と戻り値を持つ関数であれば、どの関数のアドレスも代入できると説明しました。

それを使って、乱数で2つの関数を呼び出すプログラムを作りました。

例えば、同じプログラムでも関数ポインタを使わなければ↓こうなりますよね。

<sample program 176-02>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void Func1(void);

void Func2(void);

int main(void)
{
    srand((unsigned int)time(NULL));

    if (rand() % 2) {
        Func1();
    }
    else {
        Func2();
    }

    return 0;
}

void Func1(void)
{
    printf("Enter Func1\n");
}

void Func2(void)
{
    printf("Enter Func2\n");
}

<実行結果>

Enter Func1
続行するには何かキーを押してください・・・

なぜ、わざわざ関数ポインタを使って作ったのかというと・・・

  関数を呼び出す箇所が1つで済む

これがポイントになります。


では、これを関数ポインタ配列を使ったプログラムに変えてみましょう。

<sample program 176-03>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ARRAY_MAX 2

void Func1(void);

void Func2(void);

int main(void)
{
    void(*pFunc[ARRAY_MAX])(void);

    srand((unsigned int)time(NULL));

    pFunc[0] = Func1;
    pFunc[1] = Func2;

    pFunc[rand() % 2]();

    return 0;
}

void Func1(void)
{
    printf("Enter Func1\n");
}

void Func2(void)
{
    printf("Enter Func2\n");
}

<実行結果>

Enter Func1
続行するには何かキーを押してください・・・

関数ポインタ名の後に配列の要素数を書く事で、関数ポインタ配列になります。

普通に添え字を使ってアクセスし、関数のアドレスを格納出来ます。

そして、

pFunc[0]();

のように書く事で、要素に入っている関数を呼び出すことが出来ます。


次に、例を変えて使い道について書きます。

やりたい事を↓に書きます。

ゲームにおける敵の動きについてプログラムを組みたい

・敵は10体

・パラメータに動きの種類を表す変数kindを用意する

・敵の動きの種類は3種類

・変数kindに0、1、2を格納することにより区別する

・10体分のkindの値は乱数でセットする

・3種類の動きはそれぞれ関数で用意する

・変数kindの値によって、それぞれの関数を呼び出す

さて、これを実際に作ってみましょう。

最初は関数ポインタ配列を使わずに書いてみます。

<sample program 176-04>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ENEMY_MAX 10

#define ENEMY_KIND_MAX 3

typedef struct {
    int kind;
} Enemy;

void MoveEnemy1(void);
void MoveEnemy2(void);
void MoveEnemy3(void);

int main(void)
{
    Enemy enemy[ENEMY_MAX];

    int i;

    srand((unsigned int)time(NULL));

    for (i = 0; i < ENEMY_MAX; i++) {
        enemy[i].kind = rand() % ENEMY_KIND_MAX;
    }

    for (i = 0; i < ENEMY_MAX; i++) {
        switch (enemy[i].kind) {
        case 0:
            MoveEnemy1();
            break;
        case 1:
            MoveEnemy2();
            break;
        case 2:
            MoveEnemy3();
            break;
        }
    }

    return 0;
}

void MoveEnemy1(void)
{
    printf("Enemy1 : 右に移動します。\n");
}

void MoveEnemy2(void)
{
    printf("Enemy2 : 下に移動します。\n");
}

void MoveEnemy3(void)
{
    printf("Enemy3 : 回転します。\n");
}

<実行結果>

Enemy3 : 回転します。
Enemy3 : 回転します。
Enemy2 : 下に移動します。
Enemy2 : 下に移動します。
Enemy3 : 回転します。
Enemy2 : 下に移動します。
Enemy1 : 右に移動します。
Enemy1 : 右に移動します。
Enemy2 : 下に移動します。
Enemy3 : 回転します。
続行するには何かキーを押してください・・・

敵のパラメータはkindのみとしました。

switch文を使って、配列に格納されているkindを確認し、値に合った関数を呼び出しています。

これを、関数ポインタ配列を使った形に変更します。

<sample program 176-05>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ENEMY_MAX 10

#define ENEMY_KIND_MAX 3

typedef struct {
    int kind;
} Enemy;

void MoveEnemy1(void);
void MoveEnemy2(void);
void MoveEnemy3(void);

int main(void)
{
    Enemy enemy[ENEMY_MAX];

    void(*pMoveEnemy[ENEMY_KIND_MAX])(void) = {
        MoveEnemy1,
        MoveEnemy2,
        MoveEnemy3,
    };

    int i;

    srand((unsigned int)time(NULL));

    for (i = 0; i < ENEMY_MAX; i++) {
        enemy[i].kind = rand() % ENEMY_KIND_MAX;
    }

    for (i = 0; i < ENEMY_MAX; i++) {
        pMoveEnemy[enemy[i].kind]();
    }

    return 0;
}

void MoveEnemy1(void)
{
    printf("Enemy1 : 右に移動します。\n");
}

void MoveEnemy2(void)
{
    printf("Enemy2 : 下に移動します。\n");
}

void MoveEnemy3(void)
{
    printf("Enemy3 : 回転します。\n");
}

<実行結果>

Enemy2 : 下に移動します。
Enemy3 : 回転します。
Enemy1 : 右に移動します。
Enemy1 : 右に移動します。
Enemy1 : 右に移動します。
Enemy2 : 下に移動します。
Enemy3 : 回転します。
Enemy3 : 回転します。
Enemy1 : 右に移動します。
Enemy2 : 下に移動します。
続行するには何かキーを押してください・・・

関数ポインタ配列を用意し、3つの関数のアドレスを格納しました。

※今回は初期値として設定しました。

メンバ変数kindの値によって、

  pMoveEnemy[0]();  ←  MoveEnemy1関数を呼び出す

  pMoveEnemy[1]();  ←  MoveEnemy2関数を呼び出す

  pMoveEnemy[2]();  ←  MoveEnemy3関数を呼び出す

のどれかが実行される仕組みです。


関数ポインタ配列を使ったプログラムの方が、プログラム的にもすっきりして見えます。

敵の動きを「追加」する際にも、関数ポインタ配列の方が楽に出来ます。

※switch文はプログラムが長くなる傾向があります。

配列の添え字を変える事で、呼び出す関数が変更出来るので、使いどころは色々ありそうですね。


次へ

戻る

目次へ