--- tags: [concept, c, oop, embedded, linux-kernel] type: guide created: 2026-07-23 updated: 2026-07-25 --- # C语言面向对象编程完整指南 **一句话本质**:C 没有 `class`、`private`、继承或虚函数;C-OOP 是用 `struct`、函数、首成员嵌入和函数指针表手动组织出类似面向对象的结构。C++ 编译器常用相似的底层布局实现对象模型,但 C++ 标准并不要求“先翻译成 C”。 --- ## 目录 1. [[#一、开胃菜:5 分钟看到你的第一个 C 类]] 2. [[#二、封装:创建你自己的 C 类]] 3. [[#三、继承:复用公共字段]] 4. [[#四、多态:同一个接口,不同的行为]] 5. [[#五、向下转型:container_of 原理]] 6. [[#六、四层架构:工业级项目怎么组织]] 7. [[#七、Linux 内核中的 C-OOP]] 8. [[#八、速查卡(打印贴墙用)]] --- ## 一、开胃菜:5 分钟看到你的第一个 C 类 先看三个核心对应关系,然后直接上手: ```c // C++ 概念 → C 实现 // class Student → Student.h + Student.c // private int id; → struct Student { int id; }; 写在 .c 里 // public void set() → Student.h 中声明的函数 // this->id → me->id(me 是显式第一个参数) ``` ### 完整的 Student 类(按 3 个文件复制后即可编译) 在同一个目录新建下列三个文件。这里使用 `Student_create` / `Student_destroy`,这是不透明类型的正确用法:调用方不知道 `struct Student` 的大小,因而不能在栈上写 `Student s;`。 ```c // ==================== Student.h ==================== #ifndef STUDENT_H #define STUDENT_H typedef struct Student Student; Student *Student_create(int id, int grade); void Student_destroy(Student *self); int Student_set_id(Student *self, int id); int Student_get_id(const Student *self); int Student_set_grade(Student *self, int grade); int Student_get_grade(const Student *self); void Student_print(const Student *self); #endif ``` ```c // ==================== Student.c ==================== #include "Student.h" #include #include struct Student { int id; int grade; }; static int Student_is_valid_grade(int grade) { return grade >= 0 && grade <= 100; } Student *Student_create(int id, int grade) { Student *self; if (id < 0 || !Student_is_valid_grade(grade)) return NULL; self = malloc(sizeof(*self)); if (self == NULL) return NULL; self->id = id; self->grade = grade; return self; } void Student_destroy(Student *self) { free(self); } int Student_set_id(Student *self, int id) { if (self == NULL || id < 0) return -1; self->id = id; return 0; } int Student_get_id(const Student *self) { return self == NULL ? -1 : self->id; } int Student_set_grade(Student *self, int grade) { if (self == NULL || !Student_is_valid_grade(grade)) return -1; self->grade = grade; return 0; } int Student_get_grade(const Student *self) { return self == NULL ? -1 : self->grade; } void Student_print(const Student *self) { if (self != NULL) printf("Student{id=%d, grade=%d}\n", self->id, self->grade); } ``` ```c // ==================== main.c ==================== #include "Student.h" #include #include int main(void) { Student *student = Student_create(1001, 85); assert(student != NULL); assert(Student_set_grade(student, 90) == 0); assert(Student_set_grade(student, 101) == -1); Student_print(student); Student_destroy(student); return 0; } ``` ```sh # 在这三个文件所在目录执行 gcc -std=c11 -Wall -Wextra -Werror -pedantic Student.c main.c -o student_test ./student_test ``` 预期输出:`Student{id=1001, grade=90}`。尝试写 `student->grade = 90;` 会在 `main.c` 编译失败,因为该文件只见到不完整类型。 ---------------------------------------------------------------- ## 二、封装:创建你自己的 C 类 ### 2.1 你遇到的问题 ```c // 坏代码:全局变量到处可改 int led_pin = 15; // 谁都能改 led_pin = 999 void led_on() { /* ... */ } // 另一个文件不小心: led_pin = 999; // bug!找了一下午 ``` **封装要解决的就是这个问题**:把数据和操作绑在一起,对外隐藏内部细节。 ### 2.2 C 的封装三板斧 | 手法 | C 语法 | 效果 | | ---------- | ----------------------------------- | -------------------- | | 结构体打包 | `struct` | 把相关字段捆在一起 | | 信息隐藏 | `.h` 只放声明,`struct` 定义放 `.c` | 外部无法访问成员 | | 私有化 | `static` 修饰函数/变量 | 仅本`.c` 文件可见 | | 命名空间 | 模块前缀`xxx_` | 不同模块函数不会撞名 | | 生命周期 | `init()` / `deinit()` 配对 | 规范化构造/析构 | ### 2.3 完整模板:XXX 类骨架 这是你**每次创建新类都要用的模板**。直接复制,替换 `XXX`。 ```c // ==================== XXX.h ==================== #ifndef XXX_H #define XXX_H #include #include /* ---- 不透明类型(外部无法访问内部字段) ---- */ typedef struct XXX XXX; /* ---- 构造/析构 ---- */ void XXX_init(XXX *me, int param); void XXX_deinit(XXX *me); /* ---- 公开方法(public) ---- */ int XXX_getValue(const XXX *me); void XXX_setValue(XXX *me, int v); bool XXX_isReady(const XXX *me); void XXX_reset(XXX *me); /* ---- 公开常量 ---- */ #define XXX_MAX_VALUE 255 #endif /* XXX_H */ ``` ```c // ==================== XXX.c ==================== #include "XXX.h" #include #include /* ========== private 成员变量 ========== */ struct XXX { int value; bool ready; }; /* ========== private 静态变量 ========== */ static int instance_count = 0; /* ========== private 方法 ========== */ static void validate(XXX *me) { assert(me->value >= 0 && me->value <= XXX_MAX_VALUE); } /* ========== 构造 / 析构 ========== */ void XXX_init(XXX *me, int param) { me->value = param; me->ready = true; instance_count++; validate(me); } void XXX_deinit(XXX *me) { me->ready = false; instance_count--; } /* ========== 公开方法 ========== */ int XXX_getValue(const XXX *me) { return me->value; } void XXX_setValue(XXX *me, int v) { me->value = v; validate(me); } bool XXX_isReady(const XXX *me) { return me->ready; } void XXX_reset(XXX *me) { me->value = 0; } ``` ### 2.4 实际例子:RingBuffer(环形缓冲区) 这是一个**真正有用**的类,体现封装的全部要点: ```c // ==================== RingBuffer.h ==================== #ifndef RINGBUFFER_H #define RINGBUFFER_H #include #include typedef struct RingBuffer RingBuffer; RingBuffer *RingBuffer_create(uint8_t *buf, int size); void RingBuffer_destroy(RingBuffer *me); bool RingBuffer_put(RingBuffer *me, uint8_t byte); bool RingBuffer_get(RingBuffer *me, uint8_t *byte); int RingBuffer_count(const RingBuffer *me); bool RingBuffer_isFull(const RingBuffer *me); bool RingBuffer_isEmpty(const RingBuffer *me); void RingBuffer_clear(RingBuffer *me); #endif ``` ```c // ==================== RingBuffer.c ==================== #include "RingBuffer.h" #include /* private 成员 */ struct RingBuffer { uint8_t *buf; int size; int head; // 写指针 int tail; // 读指针 }; /* private 方法 */ static int next_pos(int pos, int size) { return (pos + 1) % size; } /* 构造 */ RingBuffer *RingBuffer_create(uint8_t *buf, int size) { if (buf == NULL || size < 2) return NULL; RingBuffer *me = malloc(sizeof(*me)); if (me == NULL) return NULL; me->buf = buf; me->size = size; me->head = 0; me->tail = 0; return me; } void RingBuffer_destroy(RingBuffer *me) { free(me); } /* 公开方法 */ bool RingBuffer_put(RingBuffer *me, uint8_t byte) { if (RingBuffer_isFull(me)) return false; me->buf[me->head] = byte; me->head = next_pos(me->head, me->size); return true; } bool RingBuffer_get(RingBuffer *me, uint8_t *byte) { if (RingBuffer_isEmpty(me)) return false; *byte = me->buf[me->tail]; me->tail = next_pos(me->tail, me->size); return true; } int RingBuffer_count(const RingBuffer *me) { if (me->head >= me->tail) return me->head - me->tail; return me->size - (me->tail - me->head); } bool RingBuffer_isFull(const RingBuffer *me) { return next_pos(me->head, me->size) == me->tail; } bool RingBuffer_isEmpty(const RingBuffer *me) { return me->head == me->tail; } void RingBuffer_clear(RingBuffer *me) { me->head = me->tail = 0; } ``` ```c // ==================== main.c ==================== #include "RingBuffer.h" #include int main(void) { uint8_t mem[16]; RingBuffer *rb = RingBuffer_create(mem, sizeof(mem)); if (rb == NULL) return 1; RingBuffer_put(rb, 'H'); RingBuffer_put(rb, 'i'); uint8_t ch; while (RingBuffer_get(rb, &ch)) { putchar(ch); // → Hi } putchar('\n'); // rb.head = 999; ← 编译错误!private! RingBuffer_destroy(rb); return 0; } ``` ```sh gcc -std=c11 -Wall -Wextra -Werror -pedantic RingBuffer.c main.c -o ringbuffer_test ./ringbuffer_test ``` 预期输出:`Hi`。 ### 2.5 对照:C++/Java 的封装 vs C ``` C++ Java C ────────────────────────────────────────────────────────────────── class RingBuffer { class RingBuffer { // RingBuffer.h private: private: typedef struct RingBuffer RingBuffer; uint8_t* buf; byte[] buf; // RingBuffer.c int head, tail; int head, tail; struct RingBuffer { ... }; public: public: // RingBuffer.h void put(uint8_t b); void put(byte b); void RingBuffer_put(RingBuffer*, uint8_t); private: private: // RingBuffer.c int nextPos(int p); int nextPos(int p); static int next_pos(...); }; RingBuffer rb; RingBuffer rb = new...; RingBuffer rb; rb.put('H'); rb.put('H'); RingBuffer_put(rb, 'H'); // rb.head // rb.head // rb.head → 编译错误! ``` --- ## 三、继承:复用公共字段 ### 3.1 你遇到的问题 ```c struct DataPacket { uint32_t src; // ← 这两个字段每个包类型都有 uint32_t dst; // ← uint32_t seq_num; uint8_t payload[64]; }; struct AckPacket { uint32_t src; // ← 重复! uint32_t dst; // ← 重复! uint32_t ack_num; uint32_t window; }; // 改 src 类型?要改两个结构体! ``` ### 3.2 C 的"继承":结构体嵌套 **核心规则**:基类 struct 作为子类 struct 的**第一个成员**。 ```c // 基类:所有包的公共头 struct PacketHeader { uint32_t src; uint32_t dst; uint16_t len; }; // 子类:数据包("继承" PacketHeader) struct DataPacket { struct PacketHeader hdr; // ★ 第一个成员 = 继承 uint32_t seq_num; uint8_t payload[64]; }; // 子类:确认包 struct AckPacket { struct PacketHeader hdr; // ★ 同样继承 uint32_t ack_num; uint32_t window; }; ``` ### 3.3 内存布局详解(理解了这个就理解了继承) ```c struct PacketHeader { uint32_t src; uint32_t dst; uint16_t len; }; // 偏移: 0 4 8 10 struct DataPacket { struct PacketHeader hdr; // 偏移 0~11 uint32_t seq_num; // 偏移 12 uint8_t payload[64]; // 偏移 16 }; // 关键: struct DataPacket dp; struct PacketHeader *hp = &dp.hdr; // hp == &dp,地址完全相同! ``` ``` DataPacket 内存: 地址: 0x00 0x04 0x08 0x0C 0x10 0x50 ┌─────┬─────┬─────┬─────┬──────────┐ │ src │ dst │ len │ seq │ payload │ └─────┴─────┴─────┴─────┴──────────┘ ↑ hdr 部分 (PacketHeader) ↑ 子类特有 &dp.hdr == &dp ← 地址相同! ``` **因为地址相同**,任何需要 `PacketHeader*` 的地方都可以传入 `&dp.hdr`,这就是向上转型(类似 C++ 的 `Derived*` → `Base*`)。 ### 3.4 完整模板:Base + Derived ```c // ==================== Base.h ==================== #ifndef BASE_H #define BASE_H typedef struct { int id; char name[32]; } Base; void Base_init(Base *me, int id, const char *name); void Base_print(const Base *me); #endif ``` ```c // ==================== Base.c ==================== #include "Base.h" #include #include void Base_init(Base *me, int id, const char *name) { me->id = id; strncpy(me->name, name, sizeof(me->name) - 1); me->name[sizeof(me->name) - 1] = '\0'; } void Base_print(const Base *me) { printf("[%d] %s", me->id, me->name); } ``` ```c // ==================== Derived.h ==================== #ifndef DERIVED_H #define DERIVED_H #include "Base.h" typedef struct { Base base; // ★ 继承:必须是第一个成员 int extra; } Derived; void Derived_init(Derived *me, int id, const char *name, int extra); void Derived_print(Derived *me); // "覆盖"基类方法 #endif ``` ```c // ==================== Derived.c ==================== #include "Derived.h" #include void Derived_init(Derived *me, int id, const char *name, int extra) { Base_init(&me->base, id, name); // 先初始化基类 me->extra = extra; // 再初始化自己的 } /* 覆盖:定义同名函数,内部调用基类方法 */ void Derived_print(Derived *me) { Base_print(&me->base); // 类似 C++ 的 Base::print() printf(", extra=%d\n", me->extra); } ``` ```c // ==================== main.c ==================== #include "Derived.h" #include int main(void) { Derived d; Derived_init(&d, 1, "Alice", 999); Derived_print(&d); // → [1] Alice, extra=999 /* 向上转型:Derived* → Base*(安全,因为地址相同) */ Base *bp = &d.base; Base_print(bp); // → [1] Alice putchar('\n'); return 0; } ``` ```sh gcc -std=c11 -Wall -Wextra -Werror -pedantic Base.c Derived.c main.c -o inherit_test ./inherit_test ``` 预期输出: ```text [1] Alice, extra=999 [1] Alice ``` ### 3.5 对照 C++ ```C++ // C++ // C class Base { // Base.h public: Base(int id, const char *n); // Base_init(Base*, int, const char*) void print(); // Base_print(const Base*) private: int id; char name[32]; }; class Derived : public Base { // Derived.h: Base base 作为第一成员 public: Derived(int id, const char *n, int e) : Base(id, n), extra(e) {} // Derived_init → Base_init(&me->base,...) void print() { // Derived_print Base::print(); // Base_print(&me->base) cout << extra; } private: int extra; }; // 向上转型 Base *bp = &d; // Base *bp = &d.base; ``` --- ## 四、多态:同一个接口,不同的行为 ### 4.1 你遇到的问题 ```c void draw_shape(int type, void *shape) { if (type == 0) { // Circle Circle *c = (Circle *)shape; draw_circle(c); } else if (type == 1) { // Rect Rect *r = (Rect *)shape; draw_rect(r); } // 每加一种新形状,就要加一个 else if! } ``` ### 4.2 解决方案:ops 表(虚函数表) 核心思想: 1. 定义一个**函数指针结构体**(= C++ 的虚函数表 vtable) 2. 基类里放一个指向这个结构体的指针(= vptr) 3. 每个子类提供自己的 ops 表实例 4. 调用时通过 `obj->ops->method(obj)` 动态分发 ### 4.3 完整模板:带虚函数的基类体系 ```c // ==================== Base.h(带 vtable 的基类模板) ==================== #ifndef BASE_H #define BASE_H /* ---- 虚函数表定义(相当于 C++ 的 vtable) ---- */ typedef struct BaseOps { void (*method1)(void *me, int arg); int (*method2)(void *me); void (*destroy)(void *me); // 可选:析构 } BaseOps; /* ---- 基类 ---- */ typedef struct { const BaseOps *ops; // ★ vptr:指向子类自己的 ops 表 int id; } Base; /* ---- 统一调度接口(这是你对外调用的入口) ---- */ void Base_method1(Base *me, int arg); int Base_method2(Base *me); void Base_destroy(Base *me); #endif ``` ```c // ==================== Base.c(调度实现) ==================== #include "Base.h" #include void Base_method1(Base *me, int arg) { assert(me->ops && me->ops->method1); // 类似 C++ 纯虚函数检查 me->ops->method1(me, arg); // ★ 动态分发! } int Base_method2(Base *me) { assert(me->ops && me->ops->method2); return me->ops->method2(me); } void Base_destroy(Base *me) { if (me->ops && me->ops->destroy) me->ops->destroy(me); } ``` ```c // ==================== Derived.h(子类模板) ==================== #ifndef DERIVED_H #define DERIVED_H #include "Base.h" typedef struct { Base base; // ★ 继承基类(含 vptr) int private_data; } Derived; void Derived_init(Derived *me, int id, int data); #endif ``` ```c // ==================== Derived.c(子类实现) ==================== #include "Derived.h" #include /* ---- private 方法:子类自己的实现 ---- */ static void Derived_method1(void *me, int arg) { Derived *self = (Derived *)me; printf("Derived.method1(%d), data=%d\n", arg, self->private_data); } static int Derived_method2(void *me) { Derived *self = (Derived *)me; return self->private_data * 2; } /* ---- 子类的 ops 表(相当于 C++ 的子类 vtable) ---- */ static const BaseOps DERIVED_OPS = { .method1 = Derived_method1, .method2 = Derived_method2, .destroy = NULL, }; /* ---- 构造:关键 = 绑定 ops ---- */ void Derived_init(Derived *me, int id, int data) { me->base.ops = &DERIVED_OPS; // ★ 让 vptr 指向子类的 ops me->base.id = id; me->private_data = data; } ``` ```c // ==================== main.c(多态使用) ==================== #include "Derived.h" #include /* 这个函数完全不知道传进来的是哪个子类 */ void client_code(Base *b) { Base_method1(b, 42); // → 自动派发到子类实现 int r = Base_method2(b); printf("result=%d\n", r); } int main(void) { Derived d; Derived_init(&d, 1, 100); client_code((Base *)&d); // 向上转型 → 多态! return 0; } ``` ```sh gcc -std=c11 -Wall -Wextra -Werror -pedantic Base.c Derived.c main.c -o polymorphism_test ./polymorphism_test ``` 预期输出: ```text Derived.method1(42), data=100 result=200 ``` ### 4.4 实际例子:Stream 抽象 不同"流"有相同的读写接口,但底层实现完全不同: ```c // ==================== stream.h ==================== #ifndef STREAM_H #define STREAM_H #include typedef struct Stream Stream; typedef struct StreamOps { int (*open)(Stream *me, const char *path); void (*close)(Stream *me); size_t (*read)(Stream *me, char *buf, size_t size); size_t (*write)(Stream *me, const char *buf, size_t size); } StreamOps; struct Stream { const StreamOps *ops; }; int Stream_open(Stream *me, const char *path); void Stream_close(Stream *me); size_t Stream_read(Stream *me, char *buf, size_t size); size_t Stream_write(Stream *me, const char *buf, size_t size); #endif ``` ```c // ==================== stream.c ==================== #include "stream.h" #include int Stream_open(Stream *me, const char *path) { assert(me != NULL && me->ops != NULL && me->ops->open != NULL); return me->ops->open(me, path); } void Stream_close(Stream *me) { assert(me != NULL && me->ops != NULL && me->ops->close != NULL); me->ops->close(me); } size_t Stream_read(Stream *me, char *buf, size_t size) { assert(me != NULL && me->ops != NULL && me->ops->read != NULL); return me->ops->read(me, buf, size); } size_t Stream_write(Stream *me, const char *buf, size_t size) { assert(me != NULL && me->ops != NULL && me->ops->write != NULL); return me->ops->write(me, buf, size); } ``` ```c // ==================== memory_stream.h ==================== #ifndef MEMORY_STREAM_H #define MEMORY_STREAM_H #include "stream.h" typedef struct { Stream base; const char *data; size_t pos; } MemoryStream; void MemoryStream_init(MemoryStream *me, const char *data); #endif ``` ```c // ==================== memory_stream.c ==================== #include "memory_stream.h" #include static int memory_open(Stream *me, const char *path) { (void)me; (void)path; return 0; } static void memory_close(Stream *me) { (void)me; } static size_t memory_read(Stream *me, char *buf, size_t size) { MemoryStream *self = (MemoryStream *)me; size_t remaining = strlen(self->data) - self->pos; size_t n = remaining < size ? remaining : size; memcpy(buf, self->data + self->pos, n); self->pos += n; return n; } static size_t memory_write(Stream *me, const char *buf, size_t size) { (void)me; (void)buf; (void)size; return 0; } static const StreamOps MEMORY_STREAM_OPS = { memory_open, memory_close, memory_read, memory_write }; void MemoryStream_init(MemoryStream *me, const char *data) { me->base.ops = &MEMORY_STREAM_OPS; me->data = data; me->pos = 0; } ``` ```c // ==================== null_stream.h ==================== #ifndef NULL_STREAM_H #define NULL_STREAM_H #include "stream.h" typedef struct { Stream base; size_t bytes_written; } NullStream; void NullStream_init(NullStream *me); #endif ``` ```c // ==================== null_stream.c ==================== #include "null_stream.h" static int ns_open(Stream *me, const char *path) { (void)me; (void)path; return 0; } static void ns_close(Stream *me) { (void)me; } static size_t ns_read(Stream *me, char *buf, size_t size) { (void)me; (void)buf; (void)size; return 0; } static size_t ns_write(Stream *me, const char *buf, size_t size) { NullStream *self = (NullStream *)me; (void)buf; self->bytes_written += size; return size; } static const StreamOps NULL_STREAM_OPS = { ns_open, ns_close, ns_read, ns_write }; void NullStream_init(NullStream *me) { me->base.ops = &NULL_STREAM_OPS; me->bytes_written = 0; } ``` ```c // ==================== main.c ==================== #include "stream.h" #include "memory_stream.h" #include "null_stream.h" #include #include void copy_data(Stream *in, Stream *out) { char buf[4]; size_t n; while ((n = Stream_read(in, buf, sizeof(buf))) > 0) { assert(Stream_write(out, buf, n) == n); } } int main(void) { MemoryStream in; NullStream out; MemoryStream_init(&in, "hello"); NullStream_init(&out); assert(Stream_open((Stream *)&in, NULL) == 0); assert(Stream_open((Stream *)&out, NULL) == 0); copy_data((Stream *)&in, (Stream *)&out); printf("bytes_written=%zu\n", out.bytes_written); Stream_close((Stream *)&in); Stream_close((Stream *)&out); return 0; } ``` ```sh gcc -std=c11 -Wall -Wextra -Werror -pedantic stream.c memory_stream.c null_stream.c main.c -o stream_test ./stream_test ``` 预期输出:`bytes_written=5`。 ### 4.5 多态的底层本质 ``` C++ 编译后的内存布局: C 手动布局: ┌───────────────────────┐ ┌───────────────────────┐ │ vptr ─────→ vtable │ │ ops ─────→ ops 表 │ │ fd │ │ fd │ │ FILE* (FileStream) │ │ FILE* (FileStream) │ └───────────────────────┘ └───────────────────────┘ C++: stream->read(buf, n) → stream->vptr->read(stream, buf, n) C: Stream_read(&fs, buf, n) → fs.base.ops->read(&fs, buf, n) 完全一致。C++ 编译器帮你写的,C 里你自己写。 ``` --- ## 五、向下转型:container_of 原理 ### 5.1 问题 你已经通过 `Base *bp = &derived.base;` 向上转型了。现在你想拿回 `Derived *`。 ### 5.2 核心宏 ```c #include // 或者自己写 // 计算成员在结构体中的字节偏移 #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) // 从成员指针反推结构体指针 #define container_of(ptr, type, member) ({ \ void *__mptr = (void *)(ptr); \ ((type *)(__mptr - offsetof(type, member))); \ }) ``` **原理**:已知结构体某个成员的地址,减去该成员在结构体中的偏移量,就得到结构体的起始地址。 ``` 成员地址 → __mptr 减去 → offsetof(type, member) 得到 → 结构体起始地址 ``` ### 5.3 典型用法 ```c // 场景 1:多态回调中需要子类特有字段 static int fs_read(void *me, char *buf, int size) { // me 是 Stream*,但我们需要 FileStream 的 FILE* FileStream *self = container_of((Stream *)me, FileStream, base); return fread(buf, 1, size, self->fp); } // 场景 2:Linux 内核 workqueue struct my_device { int irq_num; struct work_struct work; // 内核结构体内嵌 }; static void my_work_handler(struct work_struct *work) { // 内核只给了 work_struct*,要拿回 my_device* struct my_device *dev = container_of(work, struct my_device, work); // 现在可以访问 dev->irq_num } ``` --- ## 六、四层架构:工业级项目怎么组织 当你有了多个类(封装)、类之间有继承、有些类需要多态,怎么组织代码? ### 6.1 标准四层 ``` ┌─────────────────────────────────────────────┐ │ app.c 应用层 │ │ 只操作基类指针,不出现任何硬件关键字 │ ├─────────────────────────────────────────────┤ │ board_init.c 板级绑定层 │ │ 实例化具体对象,绑定 ops,暴露全局指针 │ ├─────────────────────────────────────────────┤ │ xxx_subclass.c 子类实现层 │ │ 每个子类一个文件:struct + ops 表 + init │ ├─────────────────────────────────────────────┤ │ base.h/c 基类定义层 │ │ 接口 + ops 表类型 + 统一调度函数 │ └─────────────────────────────────────────────┘ ``` ### 6.2 完整四层模板 **第 1 层 — 基类(device.h/.c):** ```c // ==================== device.h ==================== #ifndef DEVICE_H #define DEVICE_H #include typedef struct DeviceOps { int (*init)(void *me); size_t (*read)(void *me, char *buf, size_t len); size_t (*write)(void *me, const char *buf, size_t len); void (*deinit)(void *me); } DeviceOps; typedef struct { const DeviceOps *ops; char name[16]; int state; // 0=closed, 1=ready } Device; int Device_init(Device *me); size_t Device_read(Device *me, char *buf, size_t len); size_t Device_write(Device *me, const char *buf, size_t len); void Device_deinit(Device *me); #endif ``` ```c // ==================== device.c ==================== #include "device.h" #include int Device_init(Device *me) { assert(me != 0 && me->ops != 0 && me->ops->init != 0); return me->ops->init(me); } size_t Device_read(Device *me, char *buf, size_t len) { assert(me != 0 && me->ops != 0 && me->ops->read != 0); return me->ops->read(me, buf, len); } size_t Device_write(Device *me, const char *buf, size_t len) { assert(me != 0 && me->ops != 0 && me->ops->write != 0); return me->ops->write(me, buf, len); } void Device_deinit(Device *me) { if (me != 0 && me->ops != 0 && me->ops->deinit != 0) me->ops->deinit(me); } ``` **第 2 层 — 子类实现(uart_device.c):** ```c // ==================== uart_device.h ==================== #ifndef UART_DEVICE_H #define UART_DEVICE_H #include "device.h" typedef struct { Device base; int uart_num; int baud; const char *rx_data; size_t rx_pos; } UartDevice; void UartDevice_init(UartDevice *me, const char *name, int uart_num, int baud, const char *rx_data); #endif ``` ```c // ==================== uart_device.c ==================== #include "uart_device.h" #include #include static int uart_init(void *me) { UartDevice *self = (UartDevice *)me; self->base.state = 1; printf("%s: UART%d init %d baud\n", self->base.name, self->uart_num, self->baud); return 0; } static size_t uart_read(void *me, char *buf, size_t len) { UartDevice *self = (UartDevice *)me; size_t left = strlen(self->rx_data) - self->rx_pos; size_t n = left < len ? left : len; memcpy(buf, self->rx_data + self->rx_pos, n); self->rx_pos += n; return n; } static size_t uart_write(void *me, const char *buf, size_t len) { UartDevice *self = (UartDevice *)me; printf("%s write: %.*s", self->base.name, (int)len, buf); return len; } static void uart_deinit(void *me) { UartDevice *self = (UartDevice *)me; self->base.state = 0; printf("%s: deinit\n", self->base.name); } static const DeviceOps UART_OPS = { .init = uart_init, .read = uart_read, .write = uart_write, .deinit = uart_deinit, }; void UartDevice_init(UartDevice *me, const char *name, int uart_num, int baud, const char *rx_data) { me->base.ops = &UART_OPS; snprintf(me->base.name, sizeof(me->base.name), "%s", name); me->base.state = 0; me->uart_num = uart_num; me->baud = baud; me->rx_data = rx_data; me->rx_pos = 0; } ``` **第 3 层 — 板级绑定(board_init.c):** ```c // ==================== board.h ==================== #ifndef BOARD_H #define BOARD_H #include "device.h" extern Device *g_console; extern Device *g_gps; void board_init(void); void board_deinit(void); #endif ``` ```c // ==================== board.c ==================== #include "uart_device.h" #include "board.h" static UartDevice console; static UartDevice gps; Device *g_console; // 全局指针——暴露给应用层 Device *g_gps; void board_init(void) { UartDevice_init(&console, "console", 1, 115200, ""); g_console = (Device *)&console; Device_init(g_console); // 调用 init ops UartDevice_init(&gps, "gps", 2, 9600, "GPS:OK\n"); g_gps = (Device *)&gps; Device_init(g_gps); } void board_deinit(void) { Device_deinit(g_console); Device_deinit(g_gps); } ``` **第 4 层 — 应用层(app.c):** ```c // ==================== app.c ==================== #include "board.h" #include #include // 完全不依赖 UART/GPIO/SPI 等任何硬件关键字 void app_main(void) { char buf[64]; size_t n; Device_write(g_console, "Hello\n", strlen("Hello\n")); n = Device_read(g_gps, buf, sizeof(buf) - 1); buf[n] = '\0'; printf("gps read: %s", buf); } ``` ```c // ==================== main.c ==================== #include "board.h" void app_main(void); int main(void) { board_init(); app_main(); board_deinit(); return 0; } ``` ```sh gcc -std=c11 -Wall -Wextra -Werror -pedantic device.c uart_device.c board.c app.c main.c -o device_test ./device_test ``` 预期输出: ```text console: UART1 init 115200 baud gps: UART2 init 9600 baud console write: Hello gps read: GPS:OK console: deinit gps: deinit ``` ### 6.3 换芯片时的改动 ``` 旧芯片 新芯片 board_init.c: board_init.c: UartDevice_init(...) → 改为新芯片的 UART 驱动 g_console = ... → 指针类型不变,名字不变 app.c: 零改动! app.c: 零改动! ``` --- ## 七、Linux 内核中的 C-OOP 这不是理论,这是 Linux 内核 4000 万行 C 代码每天都在用的模式。 ### 7.1 file_operations — 最经典的 ops 表 ```c // Linux/include/linux/fs.h struct file_operations { loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); int (*open) (struct inode *, struct file *); int (*release)(struct inode *, struct file *); // ... 几十个函数指针 }; // 每个驱动提供一个自己的 file_operations 实例 const struct file_operations ext4_file_operations = { .read = ext4_file_read, .write = ext4_file_write, .open = ext4_file_open, // ... }; const struct file_operations socket_file_ops = { .read = sock_read, .write = sock_write, // ... }; // VFS 统一调用,自动分发 ssize_t vfs_read(struct file *file, ...) { return file->f_op->read(file, buf, size, pos); // 多态! } ``` ### 7.2 container_of 在内核中的使用 ```c // Linux 设备驱动标准模式 struct my_device { int irq; struct device dev; // 内核设备模型结构体 struct work_struct work; // 工作队列 }; // 内核只回调 work 函数,用 container_of 拿回 my_device static void my_work_handler(struct work_struct *work) { struct my_device *mdev = container_of(work, struct my_device, work); complete(&mdev->done); } // probe 时注册 static int my_probe(struct platform_device *pdev) { struct my_device *mdev; mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL); INIT_WORK(&mdev->work, my_work_handler); platform_set_drvdata(pdev, mdev); } ``` ### 7.3 list_head — 侵入式链表 ```c // Linux/include/linux/types.h struct list_head { struct list_head *next, *prev; }; // 核心遍历宏 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) // 从节点反查结构体 #define list_entry(ptr, type, member) container_of(ptr, type, member) // 一步到位遍历业务对象 #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) ``` ```c // 使用例子 struct my_data { int id; struct list_head node; // 侵入式节点 }; LIST_HEAD(data_list); // 初始化链表头 // 添加 struct my_data *d = malloc(sizeof(*d)); d->id = 42; list_add_tail(&d->node, &data_list); // 遍历(无需递归、无需索引) struct my_data *pos; list_for_each_entry(pos, &data_list, node) { printf("id=%d\n", pos->id); } ``` ### 7.4 内核 C-OOP 对照表 | 内核概念 | 你学到的 C-OOP 概念 | | -------------------------- | ---------------------------------------- | | `struct file_operations` | ops 表(vtable) | | `struct i2c_algorithm` | 协议层的 ops 表 | | `struct gpio_chip` | 硬件抽象层的 ops 表 | | `container_of` | 向下转型 | | `list_head` + `list_entry` | 侵入式容器 | | `module_init` | 自动注册(`__attribute__((section()))`) | | `platform_driver.probe` | 构造函数 + 绑定 | | `devm_kzalloc` | 构造中分配资源 | --- ## 八、速查卡(打印贴墙用) ### 8.1 创建一个新类的步骤 ``` 1. 复制模板(见 §2.3 XXX.h + XXX.c) 2. 全局替换 XXX → 你的类名 3. 在 struct XXX { ... }; 中加你的字段 4. 实现 init/deinit 和各方法 5. #include 并使用 ``` ### 8.2 C++/Java → C 速查表 ``` C++/Java 概念 → C 实现 ──────────────────────────────────────────────── class Student { → Student.h + Student.c private: int id; → struct Student { int id; }; (在 .c 里) public: void setId(int); → Student.h 中:void Student_setId(Student*, int); this->id = id; → me->id = id; Student(int id, int g) → void Student_init(Student*, int, int); ~Student() → void Student_deinit(Student*); 继承:class B : public A → struct B { A base; int extra; };(A 是第一成员) super.method(); → A_method(&me->base); virtual void foo() = 0; → ops 表 + assert(ops->foo != NULL) 多态调用:a->foo() → me->ops->foo(me); namespace XXX → 函数前缀 XXX_(例:XXX_init) 模板:List → 宏 + void* 异常 try/catch → 返回错误码 + assert ``` ### 8.3 关键字用法 | 关键字 | 在 C-OOP 中的角色 | | ------------------ | ------------------------------------------------ | | `struct` | 定义对象的属性集合(= C++ class 的成员变量) | | `static`(文件域) | private 函数/变量(仅本 .c 可见) | | `static`(函数内) | 跨调用保持状态的局部变量 | | `const` | 只读常量/参数表 | | `typedef` | 隐藏`struct` 关键字,简化类型名 | | `extern` | 声明其他文件定义的全局变量 | | `void *` | ops 回调中的泛型指针("不知道具体类型,先拿着") | | `__attribute__` | GCC 扩展:段控制、对齐、弱符号等 | ### 8.4 常用宏 ```c /* 成员偏移量 */ #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) /* 成员指针 → 结构体指针 */ #define container_of(ptr, type, member) ({ \ void *__mptr = (void *)(ptr); \ ((type *)(__mptr - offsetof(type, member))); \ }) /* 遍历链表 */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #define list_entry(ptr, type, member) container_of(ptr, type, member) #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /* 数组长度 */ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) /* 安全 max/min */ #define MAX(a,b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); _a > _b ? _a : _b; }) #define MIN(a,b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); _a < _b ? _a : _b; }) /* 位操作 */ #define BIT(n) (1UL << (n)) #define SET_BIT(reg, n) ((reg) |= BIT(n)) #define CLR_BIT(reg, n) ((reg) &= ~BIT(n)) /* 自动注册(裸机版) */ #define __init_call __attribute__((section(".initcall"))) #define MODULE_INIT(fn) static void (*__init_##fn)(void) __init_call = fn ``` > 核验资料: > > - C 结构体首成员地址规则:[WG14 C11 草案 N1570](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) `6.7.2.1`。 > - GNU C 扩展:[GCC statement expressions](https://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc/Statement-Exprs.html) 与 [GCC `typeof`](https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Typeof.html)。 > - Linux `container_of`:[内核设计文档](https://www.kernel.org/doc/Documentation/driver-model/design-patterns.txt)说明成员必须是实际内嵌成员;[当前内核头文件](https://github.com/torvalds/linux/blob/master/include/linux/container_of.h)建议新代码优先使用 `container_of_const()`。 > > 来源:B 站课程《C语言OOP封装完整系列》(500强嵌入式工程师) + Linux 内核源码 > 关联笔记:[[C++类与对象]] 对比 C++ class 底层原理