2026-08-23
calc_dispatch.c
typedef int (*op_fn)(int);
int square(int n) { return n * n; }
int cube(int n) { return n * n * n; }
typedef struct {
op_fn table[2];
int cache[16];
unsigned char cached[16];
} calc_t;
int calc_run(calc_t *c, int op, int n) {
if (!c->cached[n]) {
c->cache[n] = c->table[op](n);
c->cached[n] = 1;
}
return c->cache[n];
}
int main(void) {
calc_t c = { .table = { square, cube } };
printf("%d\n", calc_run(&c, 0, 3));
printf("%d\n", calc_run(&c, 1, 3));
}
c->table[op]でsquareかcubeかを実行時に選んでいます(動的ディスパッチ)。1行目は9で正しいのですが、cube(3)で27を出すはずの2行目も9になってしまいます。何が問題で、どう直せばよいでしょうか?
Answer
cacheとcachedがnだけをキーにしていて、どのopで計算したかを覚えていません。calc_run(&c, 0, 3)でsquare(3)を計算するとcached[3]が立ってしまい、続くcalc_run(&c, 1, 3)は同じn=3を見て「もう計算済み」と判断し、cubeを一度も呼ばずにさっきのsquareの結果を返してしまいます。動的ディスパッチでは呼ばれる関数が実行時に変わるので、メモ化のキャッシュも「引数」だけでなく「どの関数を呼んだか」まで区別しないと、別の関数の結果を使い回してしまいます。opごとに別のキャッシュを持たせれば直ります:
typedef struct {
op_fn table[2];
int cache[2][16];
unsigned char cached[2][16];
} calc_t;
int calc_run(calc_t *c, int op, int n) {
if (!c->cached[op][n]) {
c->cache[op][n] = c->table[op](n);
c->cached[op][n] = 1;
}
return c->cache[op][n];
}