You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何降低C语言结构体中函数指针的内存占用?

解决C语言模拟OOP时函数指针内存冗余问题

通用解决方案

核心思路是把所有实例共享的函数指针集合抽离出来,单独形成一个虚表(vtable),每个实例只持有一个指向该虚表的指针,而非每个实例都存储所有函数指针。这样不管新增多少个函数,每个实例仅多占用一个指针的内存,内存消耗不会随函数数量增加而线性增长。

面向对象语言的处理方式

像C++、Java这类OOP语言,本质上都依赖虚表机制解决这个问题:

  • 编译器会为包含虚函数的类自动生成一个虚表,用来存储该类所有虚函数的指针。
  • 每个类的实例对象中会隐含一个指向虚表的指针(通常称为vptr),同一类的所有实例共享同一个虚表。
  • 调用虚函数时,程序通过实例的vptr找到对应虚表,再从中取出函数指针执行。

C语言中的实现方式

1. 定义虚表结构体

先把所有共享的函数指针封装到一个单独的结构体中,也就是虚表:

// Foo的虚表结构体,存储所有共享的函数指针
typedef struct FooVTable {
    void (*func1)(void*);
    void (*func2)(void*, int);
    // 可根据需求添加更多函数指针
} FooVTable;

2. 定义实例结构体

实例结构体只保留指向虚表的指针,以及实例自身的成员变量:

// Foo的实例结构体
typedef struct Foo {
    const FooVTable* vtable; // 指向共享的虚表
    // 实例专属的成员变量
    int data;
} Foo;

3. 实现虚表中的函数

编写函数的具体逻辑,第一个参数通常是实例指针(对应OOP中的this指针):

#include <stdio.h>

void Foo_func1(void* self) {
    Foo* foo = (Foo*)self;
    printf("Foo_func1 被调用,data值:%d\n", foo->data);
}

void Foo_func2(void* self, int value) {
    Foo* foo = (Foo*)self;
    foo->data = value;
    printf("Foo_func2 被调用,data已设为:%d\n", foo->data);
}

4. 创建全局共享的虚表实例

因为所有Foo实例共享同一个虚表,所以定义一个全局常量虚表即可:

// 全局共享的Foo虚表
const FooVTable Foo_vtable = {
    .func1 = Foo_func1,
    .func2 = Foo_func2
};

5. 初始化实例

创建Foo实例时,将其vtable指针指向全局虚表,并初始化成员变量:

void Foo_init(Foo* self) {
    self->vtable = &Foo_vtable;
    self->data = 0; // 初始化成员变量
}

6. 调用实例的函数

通过实例的vtable指针找到对应函数并调用:

int main() {
    Foo foo1, foo2;
    Foo_init(&foo1);
    Foo_init(&foo2);

    foo1.vtable->func1(&foo1);
    foo2.vtable->func2(&foo2, 100);
    foo1.vtable->func2(&foo1, 200);
    foo1.vtable->func1(&foo1);

    return 0;
}

扩展:实现继承

如果需要支持继承,只需定义子类的虚表(继承父类虚表并覆盖或新增函数),子类实例的vtable指针指向子类虚表即可:

#include <string.h>

// 子类Bar的虚表结构体,继承父类虚表并扩展
typedef struct BarVTable {
    FooVTable base; // 继承父类的虚表
    void (*func3)(void*); // 子类新增函数
} BarVTable;

// Bar的实例结构体,继承父类实例成员
typedef struct Bar {
    Foo base; // 继承父类的成员
    // 子类专属成员变量
    char name[20];
} Bar;

// 子类函数实现
void Bar_func3(void* self) {
    Bar* bar = (Bar*)self;
    printf("Bar_func3 被调用,name值:%s\n", bar->name);
}

// 子类的全局虚表
const BarVTable Bar_vtable = {
    .base = Foo_vtable, // 继承父类的函数
    .func3 = Bar_func3
};

// 子类初始化函数
void Bar_init(Bar* self, const char* name) {
    Foo_init((Foo*)self); // 先初始化父类
    self->base.vtable = (const FooVTable*)&Bar_vtable; // 替换为子类虚表
    strcpy(self->name, name);
}

内容的提问来源于stack exchange,提问作者Sun of A beach

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 03:24:25