【6日でできるC言語入門】関数ポインタ

 C言語の「関数ポインタ」は、関数そのもののアドレス(場所)を変数に格納し、必要に応じて関数を切り替えて呼び出すことができる強力な機能です。
関数ポインタを利用することで、プログラムの柔軟性や拡張性が向上し、コールバック関数や関数の切り替え、配列による関数管理などが実現できます。
 ここでは、関数ポインタの基本から実践的な応用方法まで、図や表を使って分かりやすく解説し、C言語プログラミング力のレベルアップをサポートします。

1.関数ポインタの基本

1.1. 関数ポインタとは何か

関数ポインタは「関数のアドレスを保持するポインタ変数」です。
 通常、ポインタ変数はデータ(int型やchar型など)のアドレスを持ちますが、関数ポインタは関数のエントリポイント(アドレス)を持ちます。

項目説明
通常のポインタint型やchar型などの変数のアドレスを持つ。
関数ポインタ関数のアドレス(呼び出し先)を持つ。

関数ポインタの宣言方法

書式

戻り値の型 (*変数名)(引数の型リスト)

例:引数も戻り値もない関数の場合

void (*fp)();

1.2. 基本的な使い方とサンプルプログラム

サンプル:2つの関数を関数ポインタで切り替えて呼び出す

プロジェクト/ファイル名: Lesson60_1/main.c

#include <stdio.h>

// サンプル関数
void greet_hello();
void greet_bye();

int main(void) {
    void (*funcp)() = NULL;  // 関数ポインタ変数の宣言と初期化

    funcp = greet_hello;     // 関数ポインタにgreet_helloを代入
    funcp();                 // ポインタ経由で関数を呼び出し

    funcp = greet_bye;       // 関数ポインタにgreet_byeを代入
    funcp();                 // ポインタ経由で関数を呼び出し

    return 0;
}

void greet_hello() {
    printf("こんにちは!\n");
}

void greet_bye() {
    printf("さようなら!\n");
}

実行結果

こんにちは!
さようなら!

解説

  • void (*funcp)() で「引数なし・戻り値void」の関数のアドレスを格納するポインタ変数funcpを宣言
  • 関数名だけ(greet_hellogreet_bye)で関数のアドレスを取得し、funcpに代入できる。
  • funcp() で、funcpが指す関数を実行できる。

2.関数ポインタの応用

2.1. 引数や戻り値がある関数のポインタ

関数ポインタは、引数や戻り値が異なる場合、型も合わせて宣言する必要があります。

サンプル:2つの計算関数を切り替えて呼び出す

プロジェクト/ファイル名: Lesson60_2/main.c

#include <stdio.h>

// 2つの整数の和を返す関数
int add(int, int);
// 2つの整数の積を返す関数
int multiply(int, int);

int main(void) {
    int (*calc)(int, int) = add;  // 関数ポインタの宣言と初期化

    int x = 7, y = 3;
    printf("%d + %d = %d\n", x, y, calc(x, y)); // addを呼ぶ

    calc = multiply; // ポインタにmultiplyを代入
    printf("%d * %d = %d\n", x, y, calc(x, y)); // multiplyを呼ぶ

    return 0;
}

int add(int a, int b) {
    return a + b;
}
int multiply(int a, int b) {
    return a * b;
}

実行結果

7 + 3 = 10
7 * 3 = 21

解説

  • int (*calc)(int, int) は「int型引数2つ、戻り値int」の関数を指せる。
  • calc に関数名(addmultiply)を代入し、切り替えて利用できる。

2.2. 関数ポインタを引数として渡す

関数ポインタは、他の関数の引数として渡すこともできます。
これにより「コールバック処理」や「アルゴリズムの切り替え」などが実現できます。

サンプル:関数ポインタを引数にして計算を切り替える

プロジェクト/ファイル名: Lesson60_3/main.c

#include <stdio.h>

int increment(int n) {
    return n + 1;
}
int decrement(int n) {
    return n - 1;
}

void print_result(int (*op)(int), int value) {
    printf("結果:%d\n", op(value));
}

int main(void) {
    print_result(increment, 10);  // 結果:11
    print_result(decrement, 10);  // 結果:9
    return 0;
}

実行結果

結果:11
結果:9

解説

  • print_resultの第一引数はint (*op)(int)で、「int型引数・int型戻り値」の関数を受け取る
  • 関数名だけで関数を渡せる
  • 呼び出し側で任意の関数(incrementやdecrement)を選択して渡せる

3.関数ポインタの型と注意点

3.1. 型一致の必要性

関数ポインタに異なる型の関数を代入することはできません
宣言した関数ポインタの「戻り値の型」「引数の型」と完全一致している関数のみを代入可能です。

宣言例代入可能な関数代入不可な関数
void (*fp)();void greet_hello();int add(int, int);
int (*op)(int, int);int add(int, int);void greet_hello();

ポイント

  • 関数ポインタの型と実際の関数の型を揃えること。
  • 型が異なる場合、コンパイルエラーや未定義動作の原因になる。

3.2. 配列による関数管理

関数ポインタの配列を使うことで、複数の関数を切り替えて使うこともできます。

サンプル:関数ポインタの配列で複数関数を一括管理

プロジェクト/ファイル名: Lesson60_4/main.c

#include <stdio.h>

void hello() { printf("やあ!\n"); }
void bye()   { printf("またね!\n"); }

int main(void) {
    void (*actions[2])() = { hello, bye };
    int i;
    for (i = 0; i < 2; i++) {
        actions[i]();  // 配列から順に関数を実行
    }
    return 0;
}

実行結果

やあ!
またね!

まとめ

  • 関数ポインタは、関数そのもののアドレスを変数として扱うC言語の強力な機能
  • 戻り値の型 (*変数名)(引数リスト)の形で宣言し、型を一致させて使う
  • ポインタ経由で関数を呼び出したり、関数ポインタを引数や配列で柔軟に利用できる
  • コールバック処理や関数の切り替え、拡張性のある設計に役立つ