函数指针 + 结构体 = C 语言的“对象模型”

补充知识点:

从 C 对象模型看 JNI:一行 (*env)->CallVoidMethod 背后的系统级真相

从函数表到 JNIEnv:彻底看懂 JNI 中的二级指针、结构体函数表与 -> 语法糖

一、为什么 C 语言需要"对象模型"?

在 C 语言里,只有两种基本东西:

  • ✅ 数据(变量 / struct)

  • ✅ 函数(全局函数)

没有

  • class
  • method
  • interface
  • virtual
  • 多态

但系统软件(操作系统、虚拟机、驱动、中间件)必须要有

  • 抽象接口
  • 模块解耦
  • 运行时替换实现
  • 多态调用

于是,C 语言世界里诞生了一种"约定俗成"的结构:

👉 struct(保存状态) + 函数指针(保存行为)

这套组合,就是 C 语言的"对象模型"。

二、最小对象模型:状态 + 行为

先从一个极小例子开始,看 C 如何"造对象"。

🎯 目标:做一个"计数器对象"

我们希望它有:

  • 状态:value

  • 方法:inc() / get()

✅ C 语言实现:

cpp 复制代码
#include <stdio.h>

typedef struct Counter Counter;

struct Counter {
    int value;                              // 状态
    void (*inc)(Counter* self);             // 行为
    int  (*get)(Counter* self);
};

void counter_inc(Counter* self) {
    self->value++;
}

int counter_get(Counter* self) {
    return self->value;
}

int main() {
    Counter c;
    c.value = 0;
    c.inc = counter_inc;
    c.get = counter_get;

    c.inc(&c);
    c.inc(&c);
    printf("%d\n", c.get(&c)); // 2
}

🔍 这里发生了什么?

  • struct Counter 保存状态
  • 函数指针保存"方法"
  • self 参数 = C 里的 this
  • c.inc(&c) = 对象调用方法

👉 这已经是一个完整意义上的"对象"

三、为什么函数指针一定要带 self?

因为 C 的函数不属于任何对象。

void counter_inc(Counter* self)

是普通函数,不知道"我是谁"。

所以:

👉 必须手动把对象传进去。

这在系统层极其常见:

cpp 复制代码
int (*read)(struct Device* dev, void* buf, int len);
int (*start)(struct Engine* engine);

第一个参数,几乎永远是:

  • self
  • ctx
  • handle
  • object

👉 它就是"对象上下文"。

四、对象能力从哪来?三大核心特性

✅ 1. 封装(Encapsulation)

调用方不直接操作内部数据,只通过函数指针访问:

c.inc(&c);

✅ 2. 多态(Polymorphism)

同一个结构体接口,可以绑定不同实现:

cpp 复制代码
void inc1(Counter* c) { c->value += 1; }
void inc2(Counter* c) { c->value += 2; }

c.inc = inc1; // 普通计数器
c.inc = inc2; // 加速计数器

👉 调用代码不变,行为变化。

✅ 3. 解耦(Decoupling)

调用方不关心:

  • 函数叫什么
  • 函数在哪
  • 如何实现

它只认:

👉 这个 struct 里有哪些"能力"。

五、进阶:vtable(虚函数表)模型

系统层通常不会把函数指针直接塞进对象,

而是拆成两层:

  • 对象:状态

  • vtable:方法表(共享)

✅ 结构升级版:

cpp 复制代码
typedef struct Counter Counter;
typedef struct CounterVTable CounterVTable;

struct CounterVTable {
    void (*inc)(Counter* self);
    int  (*get)(Counter* self);
};

struct Counter {
    int value;
    const CounterVTable* vptr;
};

✅ 实现:

cpp 复制代码
void counter_inc(Counter* self) { self->value++; }
int  counter_get(Counter* self) { return self->value; }

const CounterVTable COUNTER_VT = {
    .inc = counter_inc,
    .get = counter_get
};

✅ 使用:

cpp 复制代码
Counter c = { .value = 0, .vptr = &COUNTER_VT };
c.vptr->inc(&c);
printf("%d\n", c.vptr->get(&c));

👉 这已经和 C++ 虚函数机制几乎完全一致

  • 对象里有 vptr
  • vptr 指向函数表
  • 调用 = 查表 + 跳转

六、这套模型正是系统世界的通用设计

✅ Linux 内核

cpp 复制代码
struct file_operations {
    int (*open)(struct inode*, struct file*);
    ssize_t (*read)(struct file*, char*, size_t, loff_t*);
    ssize_t (*write)(...);
};

不同文件系统 → 填不同 ops 表。

✅ HAL / 驱动 / 中间件

cpp 复制代码
struct audio_hw_device {
    int (*init)(...);
    int (*start)(...);
    int (*stop)(...);
};

✅ JNI(你非常熟)

cpp 复制代码
struct JNINativeInterface_ {
    jclass (*FindClass)(JNIEnv*, const char*);
    jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
    ...
};

(*env)->FindClass(env, "java/lang/String");

👉 典型:vtable + self + 查表调用。

七、统一抽象视角(非常重要)

以后你看到任何系统接口,只要问三件事:

  1. 这个 struct 是不是"对象"?
  2. 这些函数指针是不是"方法表"?
  3. 第一个参数是不是 self / ctx?

如果是 →

👉 这就是 C 语言对象模型。

八、和 C++ / Java 的一一对应

C 系统层 C++ / Java
struct class
函数指针 method
self 参数 this
vtable 虚函数表
ops 结构体 interface
运行时赋值 多态

👉 所谓"面向对象",在底层几乎都落回这一套。

九、一句话系统级总结

C 没有类,

但用 struct 保存状态,

用函数指针保存行为,

用 vtable 共享方法,

就造出了整个系统世界。

Linux、Android、JVM、驱动、NDK,没有例外。

下一篇:

从 C 对象模型 → JNI → HAL → Linux 内核接口------一条贯穿系统软件的完整认知链

相关推荐
REDcker8 小时前
RTCP 刀尖点跟随技术详解
c++·机器人·操作系统·嵌入式·c·数控·机床
消失的旧时光-19432 天前
函数指针 + 结构体 = C 语言的“对象模型”?——从 C 到 C++ / Java 的本质统一
linux·c语言·开发语言·c++·c
埃伊蟹黄面2 天前
ELF深入解剖:从文件头到动态段,图解库的二进制构成
linux·c·
REDcker2 天前
AIGCJson 库解析行为与异常处理指南
c++·json·aigc·c
小张心绪烂尾6 天前
C语音的几个定义函数的题
c
程芯带你刷C语言简单算法题8 天前
Day43~实现一个算法求一个数字的树根
c语言·开发语言·算法·c
wzfj123459 天前
Opaque Pointer / Incomplete Type
c++·算法·c
charlie11451419111 天前
FreeRTOS:中断(ISR)与 RTOS 安全 API
开发语言·c·freertos·实时操作系统
charlie11451419111 天前
FreeRTOS: 信号量(Semaphores)、互斥量(Mutex)与优先级继承
开发语言·笔记·学习·c·freertos·实时操作系统