2019年4月5日金曜日

C 言語で関数ポインタを使ってみた

概要

関数の引数などのに関数を渡したい場合は関数ポインタを使います
試してみたのでサンプルコードを紹介します

環境

  • macOS 10.14.3
  • clang 10.0.1

とりあえずサンプルコード

関数ポインタを管理する pfunc を宣言しそこに print_msg 関数のアドレスを格納します
そしてコールする際は (*pfunc)("hello") という感じで引数付きでコールすることができます

  • vim main.c
#include <stdio.h>

void print_msg(char *msg) {
  printf("%s\n", msg);
}

int main(int argc, char **argv) {
  void (*pfunc)(char *str);
  pfunc = print_msg;
  (*pfunc)("hello");
}
  • clang main.c -o main
  • ./main

関数ポインタを引数に取る関数を作成してみる

さっきは関数ポインタを作成してただコールするだけでした
次は関数の引数に関数ポインタを取れるようにして、関数自体を引数として渡してみたいと思います

  • vim main.c
#include <stdio.h>

typedef void (* pfunc)(char *);

void print_msg(char *msg) {
  printf("%s\n", msg);
}

void call_func(pfunc func, char *msg) {
  func(msg);
}

int main(int argc, char **argv) {
  call_func(print_msg, "hello");
}
  • clang main.c -o main
  • ./main

ポイントは関数ポインタの変数を void (* pfunc)(char *) を typedef して型として定義します
こうすることで pfunc 型の関数として引数などで使用することができます (void call_func(pfunc func, char *msg) こんな感じ)

あとは受け取った関数を普通に関数として呼べば元の関数が呼べます
初めは少し見慣れない感じがしますが慣れると当然という感じがしてきます

今回は print_msg が void 型の返り値を持つので typedef で宣言した型も void を元にしていますが、もし print_msg が int を返す場合は typedef するのも typedef int (* pfunc)(char *); のようにしなければなりません

最後に

関数ポインタを使ってみました
イメージ的には Ruby のブロックや Proc かなと思います
よく共有ライブラリなどを使ってると関数を引数に渡せというのがありますが、その場合は素直に関数を定義して引数に渡せば良さそうです

ただ型指定がある場合は定義する関数の返り値をその型に合わせる必要があるのでそこは注意が必要です

参考サイト

0 件のコメント:

コメントを投稿