C语言模拟对象、方法:“函数指针+结构体“复用函数指针指向的函数体

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.barkb.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_operationsstruct device)的实现套路 ------ 函数体一份、虚表一份、数据每对象一份,复用最大化。


一句话总结:是的,函数指针只是"地址",指向的函数体被所有对象复用;真正每个对象独立的是结构体里的数据,靠显式 self 参数把数据喂给那份共享的代码。

顺带一提,这套模式在你这个 app 仓库里也有体现 ------ 比如 app业务 的表通过 TABLE_DESC/KEY/FIELD 宏描述,再由统一的 内存表 引擎代码去操作不同表的数据,思路是相通的(用一份引擎代码复用到多张表)。需要的话我可以在仓库里找几个具体例子讲。

相关推荐
老当益壮梁奶奶1 小时前
Linux软件编程学习笔记(二)Linux文件IO编程入门:从fopen到fgetc
linux·c语言·c++·笔记·学习
sunoo-2291 小时前
C 语言文件 IO 全攻略:从基础函数到实战踩坑(BMP 读取 + 词典查询)
linux·c语言·前端·笔记·vscode·学习
longxiaozhang61 小时前
C#继承:让代码少写一点,偷懒的好方法
开发语言·c#
weixin_440730501 小时前
python+request实现接口-参数化小结
开发语言·python
CHHH_HHH1 小时前
【Linux系统篇】深入解析Linux文件系统:从磁盘寻址到软硬链接
linux·服务器·开发语言·后端·ubuntu
yngsqq1 小时前
窗体快速取消
java·开发语言
XiaQ09192 小时前
Linux 常用命令学习笔记(用户管理篇):用户、组与权限的钥匙
linux·开发语言·笔记·学习
不可求~2 小时前
C++ 报错交给 AI 之前,先准备好这 8 类信息
开发语言·c++·人工智能
LuminousCPP2 小时前
栈和队列专题(四):LeetCode 232. 用栈实现队列|双栈分工 + 按需迁移 + 摊还 O(1)
c语言·数据结构·笔记·算法·leetcode
名字还没想好☜2 小时前
用 Python struct 解析二进制协议:pack/unpack、字节序与对齐踩坑
开发语言·python·二进制·struct