C言語を使っていると何が違うのか?と考えることがあります。
タイトルにも書いてある以下の2つです。
・関数(への)ポイント
・コールバック関数
この記事では、上記2つについて使い方とメモ書きです。
関数へのポインタはC言語を利用している人以外はあまり聞き馴染みのない言葉かもしれません。
もう一つのコールバック関数については聞いたことある、知っていると言う方も多いはずです。
ですが実際にいつ使うのか?どのように使うのか?となる人も多いハズです。
以下で順を追って確認していきます。
動作確認を行うための環境は、ubuntuのgccを使用しています。
関数へのポインタ
まずは、関数へのポインタについてです。
これについてはC言語を使う人以外はあなり聞き馴染みがないかと思います。
たとえば、以下のような関数があるとします。
int func (int num);
上記関数がある時、関数funcへのポインタを格納するポインタ変数は以下のように宣言できます。
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);
}
コメント