C 言語の関数ポインタ

胡金庫 2023年10月12日
  1. C 言語で関数ポインタを定義するには void (*func)() 記法を使用する
  2. 関数ポインタ配列を用いた型汎用プログラミング機能の実装
C 言語の関数ポインタ

この記事では、C 言語で関数ポインタを使用する方法を紹介します。

C 言語で関数ポインタを定義するには void (*func)() 記法を使用する

関数ポインタは、動的な関数呼び出しや、オブジェクト指向デザインや型ジェネリックプログラミングに似た独自のメソッドを含む構造体などの高度な機能を実装する C 言語プログラミングのもう一つの構成要素です。関数ポインタの宣言は複雑な構文を持つことがあることに注意してください。以下の例では、printInt 関数のアドレスを代入しているが、printInt 関数は単一の int 引数を取るが、void (*func)(void) 型の関数ポインタには func という名前の関数ポインタが代入されています。一度 func という名前の関数ポインタを定義すれば、通常の関数呼び出し表記法 func(arg) または間接参照演算子 (*func)(arg) を用いて呼び出すことができます。

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

void printInt(int x) { printf("printed from printInt: %d\n", x); }

int main() {
  int input1 = 10233;

  void (*func)(int) = printInt;

  func(input1);
  (*func)(input1);

  exit(EXIT_SUCCESS);
}

出力:

printed from printInt: 10233
printed from printDouble: 11.234000

あるいは、typedef を用いて関数ポインタの新しい型のエイリアスを定義し、コードをより読みやすくすることもできます。関数の型が異なる場合は、typedef 文を別々に定義する必要があることに注意してください。しかし、printIntprintDouble の両方の関数のアドレスは FuncPtr 型の変数に格納されています。特定の関数のアドレスは明示的な & 演算子を用いて取得することも、次の例で示されているように関数名そのものを暗黙的に代入することもできます。

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

typedef void (*FuncPtr)();

void printInt(int x) { printf("printed from printInt: %d\n", x); }

void printDouble(double x) { printf("printed from printDouble: %f\n", x); }

int main() {
  int input1 = 10233;
  double input2 = 11.234;

  FuncPtr func1 = printInt;
  FuncPtr func2 = printDouble;

  func1(input1);
  func2(input2);

  exit(EXIT_SUCCESS);
}

出力:

printed from printInt: 10233
printed from printDouble: 11.234000

関数ポインタ配列を用いた型汎用プログラミング機能の実装

他のオブジェクトと同様に、括弧 [] 記法で関数ポインタの配列を定義することができます。この配列を利用して、ランタイム中に特定の関数を簡単に選択して呼び出すことができます。キーワード _Generic を使用していることに注意してください。これは switch のような式で、制御する式の型の評価に基づいて特定のケースを選択することができます。したがって、以下のコード例では、switch 条件で渡された変数の型に応じて、対応する print 関数を呼び出すようにしています。なお、enum 型は異なるケースの定数値を定義するためにも利用されます。

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

enum TYPE { INTEGER, DOUBLE, INVALID };

#define typename(x) \
  _Generic((x), int: INTEGER, double: DOUBLE, default: INVALID)

typedef void (*FuncPtr)();

void printInt(int x) { printf("printed from printInt: %d\n", x); }

void printDouble(double x) { printf("printed from printDouble: %f\n", x); }

int main() {
  int input1 = 10233;
  double input2 = 11.234;

  FuncPtr func_ptrs[] = {printInt, printDouble};

  switch (typename(input1)) {
    case INTEGER:
      func_ptrs[INTEGER](input1);
      break;
    case DOUBLE:
      func_ptrs[DOUBLE](input1);
      break;
    case INVALID:
      printf("No corresponding type found!\n");
    default:
      break;
  }

  switch (typename(input2)) {
    case INTEGER:
      func_ptrs[INTEGER](input2);
      break;
    case DOUBLE:
      func_ptrs[DOUBLE](input2);
      break;
    case INVALID:
      printf("No corresponding type found!\n");
    default:
      break;
  }

  exit(EXIT_SUCCESS);
}

出力:

printed from printInt: 10233
printed from printDouble: 11.234000
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook

関連記事 - C Pointer