preface
c语言的函数指针+结构体 模拟对象和方法,复用函数指针指向的函数体
助手思考过程
(本轮为直接概念问答,未触发独立的 extended-thinking 块;回答基于对 C 语言对象模拟机制的直接组织:先给核心结论「复用同一份函数体」,再用可运行代码证明 a.bark == b.bark,然后强调 self 参数不可省,最后升级到 vtable 的工程做法,并关联到本仓库 业务目录 的 内存表 引擎复用思路。)
助手回复
核心结论
用「结构体存数据 + 函数指针存方法」模拟对象时:
- 函数体(代码)只有一份 ,存在于代码段(
.text),被所有"对象"共享; - 每个对象各有一份数据(结构体实例,在堆/栈上);
- 函数指针只是指向那唯一一份函数体的地址,不同对象的同名指针通常指向同一个函数。
这和 C++ 的成员函数机制其实一样:C++ 的成员函数也是一份代码,靠隐藏的 this 指针区分是哪个对象。
用代码看清楚
c
typedef struct Dog {
char name[32];
void (*bark)(struct Dog *self); // 函数指针 = 方法
} Dog;
// 唯一的一份函数体,复用给所有 Dog
void dog_bark(Dog *self) {
printf("%s: wang!\n", self->name); // 靠 self 区分数据
}
int main(void) {
Dog a = {"a", dog_bark};
Dog b = {"b", dog_bark};
a.bark(&a); // 输出 a: wang!
b.bark(&b); // 输出 b: wang!
// 关键:两个对象的函数指针指向同一地址
printf("%d\n", a.bark == b.bark); // 1,同一个函数体
}
a.bark 和 b.bark 值相同 ------ 证明代码复用了同一函数体 ,区别只在传入的 self(数据)不同。
关键点:self 参数不可省
C 没有 C++ 的隐式 this。函数体要访问"当前对象"的数据,必须显式把对象指针传进去 (惯例命名 self/this)。否则一份共享的函数体无从知道该操作哪个实例的数据。
更省内存的做法:vtable
如果每个对象都在结构体里塞一堆函数指针,指针本身也会被每个实例复制一份,浪费内存。工程上常把方法集中到一张"虚表"里,对象只存一个指向虚表的指针:
c
typedef struct Animal Animal;
typedef struct { // 虚表:全类共享一份
void (*speak)(Animal *self);
void (*eat)(Animal *self);
} AnimalVTable;
struct Animal {
const AnimalVTable *vtbl; // 只存一个指针
char name[32];
};
static const AnimalVTable DOG_VT = { dog_speak, dog_eat };
// 所有 Dog 实例的 vtbl 都指向 &DOG_VT
这就是 C++ 虚函数、以及 Linux 内核(如 file_operations、struct device)的实现套路 ------ 函数体一份、虚表一份、数据每对象一份,复用最大化。
一句话总结:是的,函数指针只是"地址",指向的函数体被所有对象复用;真正每个对象独立的是结构体里的数据,靠显式 self 参数把数据喂给那份共享的代码。
顺带一提,这套模式在你这个 app 仓库里也有体现 ------ 比如 app业务 的表通过 TABLE_DESC/KEY/FIELD 宏描述,再由统一的 内存表 引擎代码去操作不同表的数据,思路是相通的(用一份引擎代码复用到多张表)。需要的话我可以在仓库里找几个具体例子讲。