C言語を使っていると何が違うのか?と考えることがあります。
タイトルに含まれている以下の2つです。
- 関数へのポインタ
- コールバック関
この記事では、上記2つについて使い方とメモ書きです。
関数へのポインタはC言語を利用している人以外はあまり聞き馴染みのない言葉かもしれません。
もう1つのコールバック関数については聞いたことがある、知っているという方も多いはずです。
でも実際にはいつ使うの?どうやって使うの?となる人も多いハズです。
まだC言語を書き始めたころは、そう思っていました。
そのために自分自身が忘れてしまわないようにするためにメモ書きです。
動作確認を行うための環境はubuntuのgccを使います。
関数へのポインタ
まずは、関数へのポインタについてです。
これは、C言語を使う人以外はあまり聞き馴染みがなかとおもいます。
例えば、以下のような関数があるとします。
int func (int number)
上記のような関数がある時、関数へのポインタを格納するポインタ変数は以下のように宣言できます。
int (*func_p) (int);
これが関数へのポインタです。
非常にわかりにく構文ですよね。と文句を言いたくなりますが、とりあえず関数へのポインタを使用したサンプルです。
#include <stdio.h>
/*引数に1を足して返却する関数*/
int func_add(int number) {
return number + 1;
}
/*引数に1を引いて返却する関数*/
int func_sub(int number) {
return number - 1;
}
int main() {
int (*func_p) (int);
//func_addをポインタ変数にセット、実行
func_p = func_add;
int numA = func_p(2);
printf("func_add : %d\n", numA);
//func_subをポインタ変数にセット、実行
func_p = func_sub;
int numB = func_p(2);
printf("func_sub : %d\n", numB);
}
コールバック関数
コールバック関数とは、関数を呼び出し時に引数として渡される関数のことをいいます。
#include <stdio.h>
#include <string.h>
/*引数に1を足して返却するコールバック関数*/
int cb_add(int number) {
return number + 1;
}
/*引数に1を引いて返却するコールバック関数*/
int cb_sub(int number) {
return number - 1;
}
//コールバック関数を呼び出す関数
void func_cb(int num, int (*cb)(int)) {
printf("result:%d\n", cb(num));
}
int main() {
int (*func_p) (int);
char* s = "sub";
func_p = NULL;
if (strcmp("add", s) == 0) {
func_p = cb_add;
func_cb(5, func_p);
}
else if (strcmp("sub", s) == 0) {
func_p = cb_sub;
func_cb(5, func_p);
}
else {
printf("unknown command\n");
}
}
関数へのポインタとコールバック関数の違いは?
これについては私的には大きな違いはなくほぼ同意義として使われていると思います。
※あくまで私的な意見です。
私のこれまでの経験から明確に2つの言葉が使い分けられている印象はありません。
それでも分けるというのであれば、
C言語の場合には、関数のポインタをどの場面で使うかによって関数へのポインタなのかコールバック関数なのか分けることは出来ると思います。
おまけ:実践的な使い方
以前にmbedというものを使っているときにコマンドラインから実行されるコマンドを書いているコードを見たことがありました。
そこでは、関数へのポインタが使われていました。
それと似たものを最近見て(なんとなく)思い出したので以下に書いてみます。
ぼんやりとしか覚えていないため、以下のものがそれかは定かではありませんが、、、
#include <stdio.h>
#include <unistd.h>
#include <string.h>
//
//typedef
//
//科目一覧
typedef enum {
Jap = 3,
Mat = 4,
Eng = 5,
} Subject;
//生徒一覧
struct Student {
int no;
char* name;
int total;
int jap;
int mat;
int eng;
};
// グローバル変数
//科目
static Subject sub;
//点数
static int score;
//生徒
static struct Student student[] = {
{ 1, "yamada" , 0, 0, 0, 0 },
{ 2, "fijita" , 0, 0, 0, 0 },
{ 3, "tanaka" , 0, 0, 0, 0 },
{ 4, "suzuki" , 0, 0, 0, 0 },
};
//初期化メソッド
static void initScore_cb(struct Student* std) {
std->jap = 0;
std->mat = 0;
std->eng = 0;
std->total = 0;
printf("No:%d name:%s init\n", std->no, std->name);
}
//セットメソッド
static void setScore_cb(struct Student* std) {
switch(sub){
case Jap:
std->jap = score;
break;
case Mat:
std->mat = score;
break;
case Eng:
std->eng = score;
break;
default:
return;
}
std->total = std->jap + std->mat + std->eng;
}
//結果メソッド
static void resultScore_cb(struct Student* std) {
printf("-----------------------------------\n");
printf("No:%d Name:%s TotalScore:%d\n", std->no, std->name, std->total);
}
//関数へのポインタ
typedef void (* command_func_t)(struct Student *std);
//コマンド一覧
static struct {
char* cmd;
command_func_t func;
char *doc;
} command[] = {
{ "score-init" , initScore_cb , "init set score test" },
{ "score-set" , setScore_cb , "set score" },
{ "score-result", resultScore_cb, "result score" },
{ NULL , NULL , NULL }
};
// read command
static void readCommand(char* cmd, int no) {
for (int i = 0; command[i].cmd; i++) {
if (strcmp(command[i].cmd, cmd) == 0) {
command[i].func(&student[no]);
return;
}
}
}
// main
int main(int argc, char* argv[]) {
//初期化
readCommand("score-init", 0);
readCommand("score-init", 1);
readCommand("score-init", 2);
readCommand("score-init", 3);
//値をセット
sub = Jap;
score = 100;
readCommand("score-set", 3);
sub = Mat;
score = 88;
readCommand("score-set", 3);
sub = Eng;
score = 49;
readCommand("score-set", 3);
sub = Jap;
score = 27;
readCommand("score-set", 0);
//結果
readCommand("score-result", 0);
readCommand("score-result", 1);
readCommand("score-result", 2);
readCommand("score-result", 3);
}