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.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 宏描述,再由统一的 内存表 引擎代码去操作不同表的数据,思路是相通的(用一份引擎代码复用到多张表)。需要的话我可以在仓库里找几个具体例子讲。

相关推荐
知识分享小能手4 分钟前
C学习教程,从入门到精通,C语言学生信息管理系统 — 知识点详解(18)
c语言·学习·1024程序员节
爱读书的小胖10 分钟前
LeetCode 55 跳跃游戏 C 语言解题分析
c语言·数据结构·算法·leetcode·1024程序员节
pipixia2003327 分钟前
C++ 面试高频:namespace 的 6 个知识点,从 using 到匿名命名空间
c语言·学习·算法·嵌入式·命名空间·c++面试·匿名命名空间
2401_885885042 小时前
国际语音C#接口怎么写?.NET环境下实现国际语音验证码批量下发
开发语言·c#·.net
Navigator_Z2 小时前
LeetCode //C - 1300. Sum of Mutated Array Closest to Target
c语言·算法·leetcode
程序猿乐锅2 小时前
【从0-1学习Python | 第七篇】Python 面向对象基础
开发语言·笔记·后端·python·学习·1024程序员节
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 查询圈子个人动态:查询我的动态需求分析
java·开发语言·交友
凯瑟琳.奥古斯特2 小时前
力扣1016高效解法:剪枝优化技巧
开发语言·c++·算法·leetcode·职场和发展
坤坤子吖3 小时前
C++继承:友元、静态成员、多继承与组合
开发语言·c++·笔记·学习·1024程序员节
技术小甜甜3 小时前
【Cursor】Skill 被标黄色警告,怎么改成合格的 SKILL.md
开发语言·javascript·ai编程·cursor·1024程序员节·ai agent·skills