関数をデータとして扱う。qsort やコールバックなど、実用で必ず出会う高度なトピック。
#include <stdio.h> int square(int x) { return x * x; } int main(void) { // fp は「int を受け取り int を返す関数」へのポインタ int (*fp)(int) = square; // 関数名は自動的にアドレスに printf("%d\n", fp(5)); // → 25 printf("%d\n", (*fp)(5)); // → 25 (同等、古い書き方) return 0; }
square は式中では自動的にアドレス &square になります。つまり fp = square と fp = &square は同じ意味。呼び出しも fp(5) と (*fp)(5) は同義。fp(*fp) — fp はポインタ(int, int) — 2個のintを受ける関数int — その関数は int を返すint *fp(int); // ❌ これは関数ポインタではない! // 「int*を返す関数fpのプロトタイプ」 int (*fp)(int); // ✅ 関数ポインタ
// 比較関数の型を CmpFn という名前に typedef int (*CmpFn)(const void *, const void *); // 以後は普通の型のように使える void sort(int *a, int n, CmpFn cmp) { /* ... */ }
CmpFn x; とすれば x が関数ポインタになる。#include <stdio.h> // int 配列の各要素に対して fn を適用する汎用関数 void for_each(int *a, int n, void (*fn)(int)) { for (int i = 0; i < n; i++) { fn(a[i]); } } // コールバック例1: 表示 void print_int(int v) { printf("%d ", v); } // コールバック例2: 2乗して表示 void print_square(int v) { printf("%d ", v * v); } int main(void) { int a[] = {1, 2, 3, 4, 5}; for_each(a, 5, print_int); // 1 2 3 4 5 printf("\n"); for_each(a, 5, print_square); // 1 4 9 16 25 printf("\n"); return 0; }
for_each)と具体処理(print_int/print_square)を分離できる。イベント駆動、GUIコールバック、非同期完了通知、ソート比較などで頻出。qsort は比較関数を関数ポインタで受け取ることで、int でも文字列でも構造体でもソートできる汎用関数になっています。void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
#include <stdio.h> #include <stdlib.h> int cmp_int_asc(const void *p1, const void *p2) { int a = *(const int *)p1; int b = *(const int *)p2; // 負なら a が前、0なら同じ、正なら a が後 if (a < b) return -1; if (a > b) return 1; return 0; } int main(void) { int a[] = {5, 2, 8, 1, 9, 3}; qsort(a, 6, sizeof(int), cmp_int_asc); for (int i = 0; i < 6; i++) printf("%d ", a[i]); // → 1 2 3 5 8 9 }
return a - b; は一見動くが、a や b が大きい値や負だとオーバーフローして誤った符号になる。必ず大小比較で -1 / 1 を返すのが安全。struct Student { char name[32]; int score; }; // 得点が高い順、同点なら名前の辞書順 int cmp_student(const void *p1, const void *p2) { const struct Student *a = p1; const struct Student *b = p2; if (a->score != b->score) return (b->score > a->score) ? 1 : -1; // 降順 return strcmp(a->name, b->name); // 昇順 } // 呼び出し: qsort(students, n, sizeof(struct Student), cmp_student);
switch の代わりに使える、洗練されたパターンです。#include <stdio.h> double op_add(double a, double b) { return a + b; } double op_sub(double a, double b) { return a - b; } double op_mul(double a, double b) { return a * b; } double op_div(double a, double b) { return b != 0 ? a / b : 0; } // 演算子 → 関数ポインタ の対応表 struct OpEntry { char sym; double (*fn)(double, double); }; static const struct OpEntry ops[] = { {'+', op_add}, {'-', op_sub}, {'*', op_mul}, {'/', op_div}, }; double calc(char op, double a, double b) { for (int i = 0; i < sizeof(ops) / sizeof(ops[0]); i++) { if (ops[i].sym == op) return ops[i].fn(a, b); } return 0; } int main(void) { printf("%.2f\n", calc('+', 3, 4)); // 7.00 printf("%.2f\n", calc('*', 6, 7)); // 42.00 return 0; }
char *words[] = {"banana","apple","cherry"}; を辞書順にソートせよ。比較関数の引数型がポイント(const char **)。int filter(int *a, int n, int *out, int (*pred)(int)) を実装せよ。pred が真を返す要素だけ out にコピーし、個数を返す。偶数抽出で動作確認。