|
|
@@ -0,0 +1,1224 @@
|
|
|
+---
|
|
|
+tags: [concept, c, oop, embedded, linux-kernel]
|
|
|
+type: guide
|
|
|
+created: 2026-07-23
|
|
|
+---
|
|
|
+
|
|
|
+# C语言面向对象编程完整指南
|
|
|
+
|
|
|
+**一句话本质**:C++ 的 OOP 是编译器帮你写的 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 类(直接复制改类名就能用)
|
|
|
+
|
|
|
+```c
|
|
|
+// ==================== Student.h ====================
|
|
|
+#ifndef STUDENT_H
|
|
|
+#define STUDENT_H
|
|
|
+
|
|
|
+typedef struct Student Student; // 不透明类型 → "我有这个类,但你别管里面"
|
|
|
+
|
|
|
+void Student_init(Student *me, int id, int grade);
|
|
|
+void Student_deinit(Student *me);
|
|
|
+void Student_setId(Student *me, int id);
|
|
|
+int Student_getId(const Student *me);
|
|
|
+void Student_setGrade(Student *me, int grade);
|
|
|
+int Student_getGrade(const Student *me);
|
|
|
+void Student_print(const Student *me);
|
|
|
+
|
|
|
+#endif
|
|
|
+```
|
|
|
+
|
|
|
+```c
|
|
|
+// ==================== Student.c ====================
|
|
|
+#include "Student.h"
|
|
|
+#include <stdio.h>
|
|
|
+
|
|
|
+/* ===== 私有成员(private) ===== */
|
|
|
+struct Student {
|
|
|
+ int id; // 外部引用 s->id → 编译错误
|
|
|
+ int grade; // 外部看不见这个字段
|
|
|
+};
|
|
|
+
|
|
|
+/* ===== 私有方法(private) ===== */
|
|
|
+static int is_valid_grade(int g) {
|
|
|
+ return (g >= 0 && g <= 100);
|
|
|
+}
|
|
|
+
|
|
|
+/* ===== 私有静态变量(private static) ===== */
|
|
|
+static int total_students = 0;
|
|
|
+
|
|
|
+/* ===== 构造/析构 ===== */
|
|
|
+void Student_init(Student *me, int id, int grade) {
|
|
|
+ me->id = id;
|
|
|
+ me->grade = is_valid_grade(grade) ? grade : 0;
|
|
|
+ total_students++;
|
|
|
+}
|
|
|
+void Student_deinit(Student *me) {
|
|
|
+ total_students--;
|
|
|
+}
|
|
|
+
|
|
|
+/* ===== 公开方法(public) ===== */
|
|
|
+void Student_setId(Student *me, int id) { me->id = id; }
|
|
|
+int Student_getId(const Student *me) { return me->id; }
|
|
|
+void Student_setGrade(Student *me, int g) { if (is_valid_grade(g)) me->grade = g; }
|
|
|
+int Student_getGrade(const Student *me) { return me->grade; }
|
|
|
+void Student_print(const Student *me) {
|
|
|
+ printf("Student{id=%d, grade=%d}\n", me->id, me->grade);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+```c
|
|
|
+// ==================== main.c ====================
|
|
|
+#include "Student.h"
|
|
|
+
|
|
|
+int main(void) {
|
|
|
+ Student s; // 分配内存(但尚未初始化)
|
|
|
+ Student_init(&s, 1001, 85); // 构造
|
|
|
+ Student_setGrade(&s, 90); // 调用方法
|
|
|
+ Student_print(&s); // → Student{id=1001, grade=90}
|
|
|
+ // s.id = 999; ← 编译错误!private!
|
|
|
+ Student_deinit(&s); // 析构
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**创建自己的类只需三步**:
|
|
|
+1. 复制这 3 段代码,新建 `你的类名.h`、`你的类名.c`
|
|
|
+2. 全局替换 `Student` → 你的类名
|
|
|
+3. 在 `struct Student` 里加你的字段,在函数里写逻辑
|
|
|
+
|
|
|
+> 对应 C++:`class Student { private: int id, grade; public: ... };`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 二、封装:创建你自己的 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 <stdint.h>
|
|
|
+#include <stdbool.h>
|
|
|
+
|
|
|
+/* ---- 不透明类型(外部无法访问内部字段) ---- */
|
|
|
+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 <stdio.h>
|
|
|
+#include <assert.h>
|
|
|
+
|
|
|
+/* ========== 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 <stdint.h>
|
|
|
+#include <stdbool.h>
|
|
|
+
|
|
|
+typedef struct RingBuffer RingBuffer;
|
|
|
+
|
|
|
+void RingBuffer_init(RingBuffer *me, uint8_t *buf, int size);
|
|
|
+void RingBuffer_deinit(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 <string.h>
|
|
|
+
|
|
|
+/* 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;
|
|
|
+}
|
|
|
+
|
|
|
+/* 构造 */
|
|
|
+void RingBuffer_init(RingBuffer *me, uint8_t *buf, int size) {
|
|
|
+ me->buf = buf;
|
|
|
+ me->size = size;
|
|
|
+ me->head = 0;
|
|
|
+ me->tail = 0;
|
|
|
+}
|
|
|
+void RingBuffer_deinit(RingBuffer *me) {
|
|
|
+ me->buf = NULL; // 使用者负责释放 buf
|
|
|
+}
|
|
|
+
|
|
|
+/* 公开方法 */
|
|
|
+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 <stdio.h>
|
|
|
+
|
|
|
+int main(void) {
|
|
|
+ uint8_t mem[16];
|
|
|
+ RingBuffer rb;
|
|
|
+
|
|
|
+ RingBuffer_init(&rb, mem, sizeof(mem));
|
|
|
+
|
|
|
+ RingBuffer_put(&rb, 'H');
|
|
|
+ RingBuffer_put(&rb, 'i');
|
|
|
+
|
|
|
+ uint8_t ch;
|
|
|
+ while (RingBuffer_get(&rb, &ch)) {
|
|
|
+ putchar(ch); // → Hi
|
|
|
+ }
|
|
|
+
|
|
|
+ // rb.head = 999; ← 编译错误!private!
|
|
|
+ RingBuffer_deinit(&rb);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 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 <stdio.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+void Base_init(Base *me, int id, const char *name) {
|
|
|
+ me->id = id;
|
|
|
+ strncpy(me->name, name, sizeof(me->name) - 1);
|
|
|
+}
|
|
|
+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 <stdio.h>
|
|
|
+
|
|
|
+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", me->extra);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+```c
|
|
|
+// ==================== main.c ====================
|
|
|
+#include "Derived.h"
|
|
|
+
|
|
|
+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
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 3.5 对照 C++
|
|
|
+
|
|
|
+```cpp
|
|
|
+// 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 <assert.h>
|
|
|
+
|
|
|
+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 <stdio.h>
|
|
|
+
|
|
|
+/* ---- 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"
|
|
|
+
|
|
|
+/* 这个函数完全不知道传进来的是哪个子类 */
|
|
|
+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;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 4.4 实际例子:Stream 抽象
|
|
|
+
|
|
|
+不同"流"有相同的读写接口,但底层实现完全不同:
|
|
|
+
|
|
|
+```c
|
|
|
+// ==================== stream.h ====================
|
|
|
+typedef struct StreamOps {
|
|
|
+ int (*open)(void *me, const char *path);
|
|
|
+ void (*close)(void *me);
|
|
|
+ int (*read)(void *me, char *buf, int size);
|
|
|
+ int (*write)(void *me, const char *buf, int size);
|
|
|
+} StreamOps;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ const StreamOps *ops;
|
|
|
+ int fd;
|
|
|
+} Stream;
|
|
|
+
|
|
|
+// 统一接口
|
|
|
+int Stream_open(Stream *me, const char *path);
|
|
|
+void Stream_close(Stream *me);
|
|
|
+int Stream_read(Stream *me, char *buf, int size);
|
|
|
+int Stream_write(Stream *me, const char *buf, int size);
|
|
|
+```
|
|
|
+
|
|
|
+```c
|
|
|
+// ==================== file_stream.c ====================
|
|
|
+#include "stream.h"
|
|
|
+#include <stdio.h>
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ Stream base;
|
|
|
+ FILE *fp;
|
|
|
+} FileStream;
|
|
|
+
|
|
|
+static int fs_open(void *me, const char *path) { /* fopen */ return 0; }
|
|
|
+static void fs_close(void *me) { /* fclose */ }
|
|
|
+static int fs_read(void *me, char *b, int sz) { /* fread */ return sz; }
|
|
|
+static int fs_write(void *me, const char *b, int sz) { /* fwrite */ return sz; }
|
|
|
+
|
|
|
+static const StreamOps FILE_STREAM_OPS = {
|
|
|
+ .open = fs_open,
|
|
|
+ .close = fs_close,
|
|
|
+ .read = fs_read,
|
|
|
+ .write = fs_write,
|
|
|
+};
|
|
|
+
|
|
|
+void FileStream_init(FileStream *me) {
|
|
|
+ me->base.ops = &FILE_STREAM_OPS;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+```c
|
|
|
+// ==================== null_stream.c ====================
|
|
|
+#include "stream.h"
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ Stream base;
|
|
|
+} NullStream;
|
|
|
+
|
|
|
+static int ns_open(void *me, const char *path) { return 0; } // 啥也不做
|
|
|
+static void ns_close(void *me) { }
|
|
|
+static int ns_read(void *me, char *b, int sz) { return 0; }
|
|
|
+static int ns_write(void *me, const char *b, int sz) { return sz; } // 假装写成功
|
|
|
+
|
|
|
+static const StreamOps NULL_STREAM_OPS = {
|
|
|
+ .open = ns_open,
|
|
|
+ .close = ns_close,
|
|
|
+ .read = ns_read,
|
|
|
+ .write = ns_write,
|
|
|
+};
|
|
|
+
|
|
|
+void NullStream_init(NullStream *me) {
|
|
|
+ me->base.ops = &NULL_STREAM_OPS;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+```c
|
|
|
+// ==================== main.c ====================
|
|
|
+#include "stream.h"
|
|
|
+#include "file_stream.h"
|
|
|
+#include "null_stream.h"
|
|
|
+
|
|
|
+void copy_data(Stream *in, Stream *out) {
|
|
|
+ // 完全不知道 in/out 是文件、网络还是空流
|
|
|
+ char buf[64];
|
|
|
+ int n;
|
|
|
+ while ((n = Stream_read(in, buf, sizeof(buf))) > 0) {
|
|
|
+ Stream_write(out, buf, n);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int main(void) {
|
|
|
+ FileStream fs;
|
|
|
+ NullStream ns;
|
|
|
+
|
|
|
+ FileStream_init(&fs);
|
|
|
+ NullStream_init(&ns);
|
|
|
+
|
|
|
+ Stream_open((Stream *)&fs, "input.txt");
|
|
|
+ // NullStream 不需要真正打开
|
|
|
+
|
|
|
+ copy_data((Stream *)&fs, (Stream *)&ns);
|
|
|
+ // 从文件读,写入空流(相当于 /dev/null)
|
|
|
+
|
|
|
+ Stream_close((Stream *)&fs);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 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 <stddef.h> // 或者自己写
|
|
|
+
|
|
|
+// 计算成员在结构体中的字节偏移
|
|
|
+#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
|
|
|
+
|
|
|
+typedef struct DeviceOps {
|
|
|
+ int (*init)(void *me);
|
|
|
+ int (*read)(void *me, char *buf, int len);
|
|
|
+ int (*write)(void *me, const char *buf, int 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);
|
|
|
+int Device_read(Device *me, char *buf, int len);
|
|
|
+int Device_write(Device *me, const char *buf, int len);
|
|
|
+void Device_deinit(Device *me);
|
|
|
+
|
|
|
+#endif
|
|
|
+```
|
|
|
+
|
|
|
+```c
|
|
|
+// device.c
|
|
|
+#include "device.h"
|
|
|
+#include <assert.h>
|
|
|
+
|
|
|
+int Device_init(Device *me) {
|
|
|
+ assert(me->ops && me->ops->init);
|
|
|
+ return me->ops->init(me);
|
|
|
+}
|
|
|
+int Device_read(Device *me, char *buf, int len) {
|
|
|
+ assert(me->ops && me->ops->read);
|
|
|
+ return me->ops->read(me, buf, len);
|
|
|
+}
|
|
|
+int Device_write(Device *me, const char *buf, int len) {
|
|
|
+ assert(me->ops && me->ops->write);
|
|
|
+ return me->ops->write(me, buf, len);
|
|
|
+}
|
|
|
+void Device_deinit(Device *me) {
|
|
|
+ if (me->ops && me->ops->deinit)
|
|
|
+ me->ops->deinit(me);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**第 2 层 — 子类实现(uart_device.c):**
|
|
|
+
|
|
|
+```c
|
|
|
+// uart_device.h
|
|
|
+#include "device.h"
|
|
|
+typedef struct {
|
|
|
+ Device base;
|
|
|
+ int uart_num;
|
|
|
+ int baud;
|
|
|
+} UartDevice;
|
|
|
+
|
|
|
+void UartDevice_init(UartDevice *me, const char *name, int uart_num, int baud);
|
|
|
+```
|
|
|
+
|
|
|
+```c
|
|
|
+// uart_device.c
|
|
|
+#include "uart_device.h"
|
|
|
+#include <stdio.h>
|
|
|
+
|
|
|
+static int uart_init(void *me) { printf("UART%d init %d baud\n", ((UartDevice*)me)->uart_num, ((UartDevice*)me)->baud); return 0; }
|
|
|
+static int uart_read(void *me, char *b, int l) { /* 读 UART 寄存器 */ return l; }
|
|
|
+static int uart_write(void *me, const char *b, int l) { /* 写 UART 寄存器 */ return l; }
|
|
|
+static void uart_deinit(void *me) { printf("UART deinit\n"); }
|
|
|
+
|
|
|
+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) {
|
|
|
+ me->base.ops = &UART_OPS;
|
|
|
+ me->uart_num = uart_num;
|
|
|
+ me->baud = baud;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**第 3 层 — 板级绑定(board_init.c):**
|
|
|
+
|
|
|
+```c
|
|
|
+#include "uart_device.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);
|
|
|
+ g_gps = (Device *)&gps;
|
|
|
+ Device_init(g_gps);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**第 4 层 — 应用层(app.c):**
|
|
|
+
|
|
|
+```c
|
|
|
+// 完全不依赖 UART/GPIO/SPI 等任何硬件关键字
|
|
|
+extern Device *g_console;
|
|
|
+extern Device *g_gps;
|
|
|
+
|
|
|
+void app_main(void) {
|
|
|
+ Device_write(g_console, "Hello\n", 6);
|
|
|
+ char buf[64];
|
|
|
+ int n = Device_read(g_gps, buf, sizeof(buf));
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 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<T> → 宏 + 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
|
|
|
+```
|
|
|
+
|
|
|
+> 来源:B 站课程《C语言OOP封装完整系列》(500强嵌入式工程师) + Linux 内核源码
|
|
|
+> 关联笔记:[[C++类与对象]] 对比 C++ class 底层原理
|