Browse Source

wiki: 完善 C OOP 指南示例

OpenCode 1 month ago
parent
commit
e58b2fa9d3

+ 415 - 176
X-Knowledge-Base/raw/Joplin/嵌入式+Linux/C语言OOP封装完整系列/c-oop.md

@@ -2,11 +2,11 @@
 tags: [concept, c, oop, embedded, linux-kernel]
 tags: [concept, c, oop, embedded, linux-kernel]
 type: guide
 type: guide
 created: 2026-07-23
 created: 2026-07-23
+updated: 2026-07-25
 ---
 ---
-
 # C语言面向对象编程完整指南
 # C语言面向对象编程完整指南
 
 
-**一句话本质**:C++ 的 OOP 是编译器帮你写的 C 代码。本文教你手动写出编译器隐藏的那层代码,并获得完全控制权
+**一句话本质**:C 没有 `class`、`private`、继承或虚函数;C-OOP 是用 `struct`、函数、首成员嵌入和函数指针表手动组织出类似面向对象的结构。C++ 编译器常用相似的底层布局实现对象模型,但 C++ 标准并不要求“先翻译成 C”
 
 
 ---
 ---
 
 
@@ -35,22 +35,24 @@ created: 2026-07-23
 // this->id          →  me->id(me 是显式第一个参数)
 // this->id          →  me->id(me 是显式第一个参数)
 ```
 ```
 
 
-### 完整的 Student 类(直接复制改类名就能用)
+### 完整的 Student 类(按 3 个文件复制后即可编译)
+
+在同一个目录新建下列三个文件。这里使用 `Student_create` / `Student_destroy`,这是不透明类型的正确用法:调用方不知道 `struct Student` 的大小,因而不能在栈上写 `Student s;`。
 
 
 ```c
 ```c
 // ==================== Student.h ====================
 // ==================== Student.h ====================
 #ifndef STUDENT_H
 #ifndef STUDENT_H
 #define STUDENT_H
 #define STUDENT_H
 
 
-typedef struct Student Student;      // 不透明类型 → "我有这个类,但你别管里面"
+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);
+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
 #endif
 ```
 ```
@@ -59,64 +61,69 @@ void Student_print(const Student *me);
 // ==================== Student.c ====================
 // ==================== Student.c ====================
 #include "Student.h"
 #include "Student.h"
 #include <stdio.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 
-/* ===== 私有成员(private) ===== */
-struct Student {
-    int id;       // 外部引用 s->id → 编译错误
-    int grade;    // 外部看不见这个字段
-};
-
-/* ===== 私有方法(private) ===== */
-static int is_valid_grade(int g) {
-    return (g >= 0 && g <= 100);
-}
+struct Student { int id; int grade; };
 
 
-/* ===== 私有静态变量(private static) ===== */
-static int total_students = 0;
+static int Student_is_valid_grade(int grade) { return grade >= 0 && grade <= 100; }
 
 
-/* ===== 构造/析构 ===== */
-void Student_init(Student *me, int id, int grade) {
-    me->id = id;
-    me->grade = is_valid_grade(grade) ? grade : 0;
-    total_students++;
+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_deinit(Student *me) {
-    total_students--;
+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;
 }
 }
-
-/* ===== 公开方法(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);
+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
 ```c
 // ==================== main.c ====================
 // ==================== main.c ====================
 #include "Student.h"
 #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);           // 析构
+#include <assert.h>
+#include <stddef.h>
+
+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;
     return 0;
 }
 }
 ```
 ```
 
 
-**创建自己的类只需三步**:
-1. 复制这 3 段代码,新建 `你的类名.h`、`你的类名.c`
-2. 全局替换 `Student` → 你的类名
-3. 在 `struct Student` 里加你的字段,在函数里写逻辑
-
-> 对应 C++:`class Student { private: int id, grade; public: ... };`
+```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 类
 ## 二、封装:创建你自己的 C 类
 
 
@@ -135,13 +142,13 @@ led_pin = 999;                // bug!找了一下午
 
 
 ### 2.2 C 的封装三板斧
 ### 2.2 C 的封装三板斧
 
 
-| 手法 | C 语法 | 效果 |
-|------|--------|------|
-| 结构体打包 | `struct` | 把相关字段捆在一起 |
-| 信息隐藏 | `.h` 只放声明,`struct` 定义放 `.c` | 外部无法访问成员 |
-| 私有化 | `static` 修饰函数/变量 | 仅本 `.c` 文件可见 |
-| 命名空间 | 模块前缀 `xxx_` | 不同模块函数不会撞名 |
-| 生命周期 | `init()` / `deinit()` 配对 | 规范化构造/析构 |
+| 手法       | C 语法                                    | 效果                 |
+| ---------- | ----------------------------------------- | -------------------- |
+| 结构体打包 | `struct`                                | 把相关字段捆在一起   |
+| 信息隐藏   | `.h` 只放声明,`struct` 定义放 `.c` | 外部无法访问成员     |
+| 私有化     | `static` 修饰函数/变量                  | 仅本`.c` 文件可见  |
+| 命名空间   | 模块前缀`xxx_`                          | 不同模块函数不会撞名 |
+| 生命周期   | `init()` / `deinit()` 配对            | 规范化构造/析构      |
 
 
 ### 2.3 完整模板:XXX 类骨架
 ### 2.3 完整模板:XXX 类骨架
 
 
@@ -227,8 +234,8 @@ void XXX_reset(XXX *me)           { me->value = 0; }
 
 
 typedef struct RingBuffer RingBuffer;
 typedef struct RingBuffer RingBuffer;
 
 
-void RingBuffer_init(RingBuffer *me, uint8_t *buf, int size);
-void RingBuffer_deinit(RingBuffer *me);
+RingBuffer *RingBuffer_create(uint8_t *buf, int size);
+void RingBuffer_destroy(RingBuffer *me);
 bool RingBuffer_put(RingBuffer *me, uint8_t byte);
 bool RingBuffer_put(RingBuffer *me, uint8_t byte);
 bool RingBuffer_get(RingBuffer *me, uint8_t *byte);
 bool RingBuffer_get(RingBuffer *me, uint8_t *byte);
 int  RingBuffer_count(const RingBuffer *me);
 int  RingBuffer_count(const RingBuffer *me);
@@ -242,7 +249,7 @@ void RingBuffer_clear(RingBuffer *me);
 ```c
 ```c
 // ==================== RingBuffer.c ====================
 // ==================== RingBuffer.c ====================
 #include "RingBuffer.h"
 #include "RingBuffer.h"
-#include <string.h>
+#include <stdlib.h>
 
 
 /* private 成员 */
 /* private 成员 */
 struct RingBuffer {
 struct RingBuffer {
@@ -258,15 +265,17 @@ static int next_pos(int pos, int size) {
 }
 }
 
 
 /* 构造 */
 /* 构造 */
-void RingBuffer_init(RingBuffer *me, uint8_t *buf, int 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->buf  = buf;
     me->size = size;
     me->size = size;
     me->head = 0;
     me->head = 0;
     me->tail = 0;
     me->tail = 0;
+    return me;
 }
 }
-void RingBuffer_deinit(RingBuffer *me) {
-    me->buf = NULL;  // 使用者负责释放 buf
-}
+void RingBuffer_destroy(RingBuffer *me) { free(me); }
 
 
 /* 公开方法 */
 /* 公开方法 */
 bool RingBuffer_put(RingBuffer *me, uint8_t byte) {
 bool RingBuffer_put(RingBuffer *me, uint8_t byte) {
@@ -304,24 +313,31 @@ void RingBuffer_clear(RingBuffer *me) {
 
 
 int main(void) {
 int main(void) {
     uint8_t mem[16];
     uint8_t mem[16];
-    RingBuffer rb;
-
-    RingBuffer_init(&rb, mem, sizeof(mem));
+    RingBuffer *rb = RingBuffer_create(mem, sizeof(mem));
+    if (rb == NULL) return 1;
 
 
-    RingBuffer_put(&rb, 'H');
-    RingBuffer_put(&rb, 'i');
+    RingBuffer_put(rb, 'H');
+    RingBuffer_put(rb, 'i');
 
 
     uint8_t ch;
     uint8_t ch;
-    while (RingBuffer_get(&rb, &ch)) {
+    while (RingBuffer_get(rb, &ch)) {
         putchar(ch);              // → Hi
         putchar(ch);              // → Hi
     }
     }
+    putchar('\n');
 
 
     // rb.head = 999;             ← 编译错误!private!
     // rb.head = 999;             ← 编译错误!private!
-    RingBuffer_deinit(&rb);
+    RingBuffer_destroy(rb);
     return 0;
     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
 ### 2.5 对照:C++/Java 的封装 vs C
 
 
 ```
 ```
@@ -338,7 +354,7 @@ private:                    private:                    // RingBuffer.c
 };
 };
 
 
 RingBuffer rb;              RingBuffer rb = new...;    RingBuffer rb;
 RingBuffer rb;              RingBuffer rb = new...;    RingBuffer rb;
-rb.put('H');                rb.put('H');               RingBuffer_put(&rb, 'H');
+rb.put('H');                rb.put('H');               RingBuffer_put(rb, 'H');
 // rb.head                   // rb.head                 // rb.head → 编译错误!
 // rb.head                   // rb.head                 // rb.head → 编译错误!
 ```
 ```
 
 
@@ -447,6 +463,7 @@ void Base_print(const Base *me);
 void Base_init(Base *me, int id, const char *name) {
 void Base_init(Base *me, int id, const char *name) {
     me->id = id;
     me->id = id;
     strncpy(me->name, name, sizeof(me->name) - 1);
     strncpy(me->name, name, sizeof(me->name) - 1);
+    me->name[sizeof(me->name) - 1] = '\0';
 }
 }
 void Base_print(const Base *me) {
 void Base_print(const Base *me) {
     printf("[%d] %s", me->id, me->name);
     printf("[%d] %s", me->id, me->name);
@@ -484,13 +501,14 @@ void Derived_init(Derived *me, int id, const char *name, int extra) {
 /* 覆盖:定义同名函数,内部调用基类方法 */
 /* 覆盖:定义同名函数,内部调用基类方法 */
 void Derived_print(Derived *me) {
 void Derived_print(Derived *me) {
     Base_print(&me->base);              // 类似 C++ 的 Base::print()
     Base_print(&me->base);              // 类似 C++ 的 Base::print()
-    printf(", extra=%d", me->extra);
+    printf(", extra=%d\n", me->extra);
 }
 }
 ```
 ```
 
 
 ```c
 ```c
 // ==================== main.c ====================
 // ==================== main.c ====================
 #include "Derived.h"
 #include "Derived.h"
+#include <stdio.h>
 
 
 int main(void) {
 int main(void) {
     Derived d;
     Derived d;
@@ -500,10 +518,23 @@ int main(void) {
     /* 向上转型:Derived* → Base*(安全,因为地址相同) */
     /* 向上转型:Derived* → Base*(安全,因为地址相同) */
     Base *bp = &d.base;
     Base *bp = &d.base;
     Base_print(bp);               // → [1] Alice
     Base_print(bp);               // → [1] Alice
+    putchar('\n');
     return 0;
     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++
 ### 3.5 对照 C++
 
 
 ```cpp
 ```cpp
@@ -554,6 +585,7 @@ void draw_shape(int type, void *shape) {
 ### 4.2 解决方案:ops 表(虚函数表)
 ### 4.2 解决方案:ops 表(虚函数表)
 
 
 核心思想:
 核心思想:
+
 1. 定义一个**函数指针结构体**(= C++ 的虚函数表 vtable)
 1. 定义一个**函数指针结构体**(= C++ 的虚函数表 vtable)
 2. 基类里放一个指向这个结构体的指针(= vptr)
 2. 基类里放一个指向这个结构体的指针(= vptr)
 3. 每个子类提供自己的 ops 表实例
 3. 每个子类提供自己的 ops 表实例
@@ -658,6 +690,7 @@ void Derived_init(Derived *me, int id, int data) {
 ```c
 ```c
 // ==================== main.c(多态使用) ====================
 // ==================== main.c(多态使用) ====================
 #include "Derived.h"
 #include "Derived.h"
+#include <stdio.h>
 
 
 /* 这个函数完全不知道传进来的是哪个子类 */
 /* 这个函数完全不知道传进来的是哪个子类 */
 void client_code(Base *b) {
 void client_code(Base *b) {
@@ -675,116 +708,222 @@ int main(void) {
 }
 }
 ```
 ```
 
 
+```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 抽象
 ### 4.4 实际例子:Stream 抽象
 
 
 不同"流"有相同的读写接口,但底层实现完全不同:
 不同"流"有相同的读写接口,但底层实现完全不同:
 
 
 ```c
 ```c
 // ==================== stream.h ====================
 // ==================== stream.h ====================
+#ifndef STREAM_H
+#define STREAM_H
+
+#include <stddef.h>
+
+typedef struct Stream Stream;
 typedef struct StreamOps {
 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);
+    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;
 } StreamOps;
 
 
-typedef struct {
+struct Stream {
     const StreamOps *ops;
     const StreamOps *ops;
-    int  fd;
-} Stream;
+};
 
 
-// 统一接口
 int  Stream_open(Stream *me, const char *path);
 int  Stream_open(Stream *me, const char *path);
 void Stream_close(Stream *me);
 void Stream_close(Stream *me);
-int  Stream_read(Stream *me, char *buf, int size);
-int  Stream_write(Stream *me, const char *buf, int size);
+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
 ```c
-// ==================== file_stream.c ====================
+// ==================== stream.c ====================
+#include "stream.h"
+#include <assert.h>
+
+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"
 #include "stream.h"
-#include <stdio.h>
 
 
 typedef struct {
 typedef struct {
     Stream base;
     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,
+    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 <string.h>
+
+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 FileStream_init(FileStream *me) {
-    me->base.ops = &FILE_STREAM_OPS;
+void MemoryStream_init(MemoryStream *me, const char *data) {
+    me->base.ops = &MEMORY_STREAM_OPS;
+    me->data = data;
+    me->pos = 0;
 }
 }
 ```
 ```
 
 
 ```c
 ```c
-// ==================== null_stream.c ====================
+// ==================== null_stream.h ====================
+#ifndef NULL_STREAM_H
+#define NULL_STREAM_H
+
 #include "stream.h"
 #include "stream.h"
 
 
 typedef struct {
 typedef struct {
     Stream base;
     Stream base;
+    size_t bytes_written;
 } NullStream;
 } 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; } // 假装写成功
+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 = {
 static const StreamOps NULL_STREAM_OPS = {
-    .open  = ns_open,
-    .close = ns_close,
-    .read  = ns_read,
-    .write = ns_write,
+    ns_open, ns_close, ns_read, ns_write
 };
 };
 
 
 void NullStream_init(NullStream *me) {
 void NullStream_init(NullStream *me) {
     me->base.ops = &NULL_STREAM_OPS;
     me->base.ops = &NULL_STREAM_OPS;
+    me->bytes_written = 0;
 }
 }
 ```
 ```
 
 
 ```c
 ```c
 // ==================== main.c ====================
 // ==================== main.c ====================
 #include "stream.h"
 #include "stream.h"
-#include "file_stream.h"
+#include "memory_stream.h"
 #include "null_stream.h"
 #include "null_stream.h"
+#include <assert.h>
+#include <stdio.h>
 
 
 void copy_data(Stream *in, Stream *out) {
 void copy_data(Stream *in, Stream *out) {
-    // 完全不知道 in/out 是文件、网络还是空流
-    char buf[64];
-    int n;
+    char buf[4];
+    size_t n;
     while ((n = Stream_read(in, buf, sizeof(buf))) > 0) {
     while ((n = Stream_read(in, buf, sizeof(buf))) > 0) {
-        Stream_write(out, buf, n);
+        assert(Stream_write(out, buf, n) == n);
     }
     }
 }
 }
 
 
 int main(void) {
 int main(void) {
-    FileStream fs;
-    NullStream ns;
-
-    FileStream_init(&fs);
-    NullStream_init(&ns);
+    MemoryStream in;
+    NullStream out;
 
 
-    Stream_open((Stream *)&fs, "input.txt");
-    // NullStream 不需要真正打开
+    MemoryStream_init(&in, "hello");
+    NullStream_init(&out);
 
 
-    copy_data((Stream *)&fs, (Stream *)&ns);
-    // 从文件读,写入空流(相当于 /dev/null)
+    assert(Stream_open((Stream *)&in, NULL) == 0);
+    assert(Stream_open((Stream *)&out, NULL) == 0);
+    copy_data((Stream *)&in, (Stream *)&out);
 
 
-    Stream_close((Stream *)&fs);
+    printf("bytes_written=%zu\n", out.bytes_written);
+    Stream_close((Stream *)&in);
+    Stream_close((Stream *)&out);
     return 0;
     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 多态的底层本质
 ### 4.5 多态的底层本质
 
 
 ```
 ```
@@ -883,15 +1022,17 @@ static void my_work_handler(struct work_struct *work) {
 **第 1 层 — 基类(device.h/.c):**
 **第 1 层 — 基类(device.h/.c):**
 
 
 ```c
 ```c
-// device.h
+// ==================== device.h ====================
 #ifndef DEVICE_H
 #ifndef DEVICE_H
 #define DEVICE_H
 #define DEVICE_H
 
 
+#include <stddef.h>
+
 typedef struct DeviceOps {
 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);
+    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;
 } DeviceOps;
 
 
 typedef struct {
 typedef struct {
@@ -901,32 +1042,32 @@ typedef struct {
 } Device;
 } Device;
 
 
 int  Device_init(Device *me);
 int  Device_init(Device *me);
-int  Device_read(Device *me, char *buf, int len);
-int  Device_write(Device *me, const char *buf, int len);
+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);
 void Device_deinit(Device *me);
 
 
 #endif
 #endif
 ```
 ```
 
 
 ```c
 ```c
-// device.c
+// ==================== device.c ====================
 #include "device.h"
 #include "device.h"
 #include <assert.h>
 #include <assert.h>
 
 
 int Device_init(Device *me) {
 int Device_init(Device *me) {
-    assert(me->ops && me->ops->init);
+    assert(me != 0 && me->ops != 0 && me->ops->init != 0);
     return me->ops->init(me);
     return me->ops->init(me);
 }
 }
-int Device_read(Device *me, char *buf, int len) {
-    assert(me->ops && me->ops->read);
+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);
     return me->ops->read(me, buf, len);
 }
 }
-int Device_write(Device *me, const char *buf, int len) {
-    assert(me->ops && me->ops->write);
+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);
     return me->ops->write(me, buf, len);
 }
 }
 void Device_deinit(Device *me) {
 void Device_deinit(Device *me) {
-    if (me->ops && me->ops->deinit)
+    if (me != 0 && me->ops != 0 && me->ops->deinit != 0)
         me->ops->deinit(me);
         me->ops->deinit(me);
 }
 }
 ```
 ```
@@ -934,26 +1075,55 @@ void Device_deinit(Device *me) {
 **第 2 层 — 子类实现(uart_device.c):**
 **第 2 层 — 子类实现(uart_device.c):**
 
 
 ```c
 ```c
-// uart_device.h
+// ==================== uart_device.h ====================
+#ifndef UART_DEVICE_H
+#define UART_DEVICE_H
+
 #include "device.h"
 #include "device.h"
+
 typedef struct {
 typedef struct {
     Device base;
     Device base;
     int    uart_num;
     int    uart_num;
     int    baud;
     int    baud;
+    const char *rx_data;
+    size_t rx_pos;
 } UartDevice;
 } UartDevice;
 
 
-void UartDevice_init(UartDevice *me, const char *name, int uart_num, int baud);
+void UartDevice_init(UartDevice *me, const char *name, int uart_num, int baud, const char *rx_data);
+
+#endif
 ```
 ```
 
 
 ```c
 ```c
-// uart_device.c
+// ==================== uart_device.c ====================
 #include "uart_device.h"
 #include "uart_device.h"
 #include <stdio.h>
 #include <stdio.h>
+#include <string.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 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 = {
 static const DeviceOps UART_OPS = {
     .init   = uart_init,
     .init   = uart_init,
@@ -962,17 +1132,39 @@ static const DeviceOps UART_OPS = {
     .deinit = uart_deinit,
     .deinit = uart_deinit,
 };
 };
 
 
-void UartDevice_init(UartDevice *me, const char *name, int uart_num, int baud) {
+void UartDevice_init(UartDevice *me, const char *name, int uart_num, int baud, const char *rx_data) {
     me->base.ops = &UART_OPS;
     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->uart_num = uart_num;
     me->baud = baud;
     me->baud = baud;
+    me->rx_data = rx_data;
+    me->rx_pos = 0;
 }
 }
 ```
 ```
 
 
 **第 3 层 — 板级绑定(board_init.c):**
 **第 3 层 — 板级绑定(board_init.c):**
 
 
 ```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 "uart_device.h"
+#include "board.h"
 
 
 static UartDevice console;
 static UartDevice console;
 static UartDevice gps;
 static UartDevice gps;
@@ -981,30 +1173,71 @@ Device *g_console;      // 全局指针——暴露给应用层
 Device *g_gps;
 Device *g_gps;
 
 
 void board_init(void) {
 void board_init(void) {
-    UartDevice_init(&console, "console", 1, 115200);
+    UartDevice_init(&console, "console", 1, 115200, "");
     g_console = (Device *)&console;
     g_console = (Device *)&console;
     Device_init(g_console);   // 调用 init ops
     Device_init(g_console);   // 调用 init ops
 
 
-    UartDevice_init(&gps, "gps", 2, 9600);
+    UartDevice_init(&gps, "gps", 2, 9600, "GPS:OK\n");
     g_gps = (Device *)&gps;
     g_gps = (Device *)&gps;
     Device_init(g_gps);
     Device_init(g_gps);
 }
 }
+
+void board_deinit(void) {
+    Device_deinit(g_console);
+    Device_deinit(g_gps);
+}
 ```
 ```
 
 
 **第 4 层 — 应用层(app.c):**
 **第 4 层 — 应用层(app.c):**
 
 
 ```c
 ```c
-// 完全不依赖 UART/GPIO/SPI 等任何硬件关键字
-extern Device *g_console;
-extern Device *g_gps;
+// ==================== app.c ====================
+#include "board.h"
+#include <stdio.h>
+#include <string.h>
 
 
+// 完全不依赖 UART/GPIO/SPI 等任何硬件关键字
 void app_main(void) {
 void app_main(void) {
-    Device_write(g_console, "Hello\n", 6);
     char buf[64];
     char buf[64];
-    int n = Device_read(g_gps, buf, sizeof(buf));
+    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 换芯片时的改动
 ### 6.3 换芯片时的改动
 
 
 ```
 ```
@@ -1124,16 +1357,16 @@ list_for_each_entry(pos, &data_list, node) {
 
 
 ### 7.4 内核 C-OOP 对照表
 ### 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` | 构造中分配资源 |
+| 内核概念                       | 你学到的 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`               | 构造中分配资源                             |
 
 
 ---
 ---
 
 
@@ -1171,16 +1404,16 @@ namespace XXX               →  函数前缀 XXX_(例:XXX_init)
 
 
 ### 8.3 关键字用法
 ### 8.3 关键字用法
 
 
-| 关键字 | 在 C-OOP 中的角色 |
-|--------|-----------------|
-| `struct` | 定义对象的属性集合(= C++ class 的成员变量) |
-| `static`(文件域) | private 函数/变量(仅本 .c 可见) |
-| `static`(函数内) | 跨调用保持状态的局部变量 |
-| `const` | 只读常量/参数表 |
-| `typedef` | 隐藏 `struct` 关键字,简化类型名 |
-| `extern` | 声明其他文件定义的全局变量 |
-| `void *` | ops 回调中的泛型指针("不知道具体类型,先拿着") |
-| `__attribute__` | GCC 扩展:段控制、对齐、弱符号等 |
+| 关键字               | 在 C-OOP 中的角色                                |
+| -------------------- | ------------------------------------------------ |
+| `struct`           | 定义对象的属性集合(= C++ class 的成员变量)     |
+| `static`(文件域) | private 函数/变量(仅本 .c 可见)                |
+| `static`(函数内) | 跨调用保持状态的局部变量                         |
+| `const`            | 只读常量/参数表                                  |
+| `typedef`          | 隐藏`struct` 关键字,简化类型名                |
+| `extern`           | 声明其他文件定义的全局变量                       |
+| `void *`           | ops 回调中的泛型指针("不知道具体类型,先拿着") |
+| `__attribute__`    | GCC 扩展:段控制、对齐、弱符号等                 |
 
 
 ### 8.4 常用宏
 ### 8.4 常用宏
 
 
@@ -1220,5 +1453,11 @@ namespace XXX               →  函数前缀 XXX_(例:XXX_init)
 #define MODULE_INIT(fn)  static void (*__init_##fn)(void) __init_call = fn
 #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 内核源码
 > 来源:B 站课程《C语言OOP封装完整系列》(500强嵌入式工程师) + Linux 内核源码
 > 关联笔记:[[C++类与对象]] 对比 C++ class 底层原理
 > 关联笔记:[[C++类与对象]] 对比 C++ class 底层原理

+ 1 - 1
X-Knowledge-Base/wiki/index.md

@@ -75,4 +75,4 @@ Wiki 页面(高级篇 + 扩展篇 — 含代码准确性审计):
 在纯 C 中实现封装、继承、多态三大 OOP 特性,对标 Linux 内核与工业嵌入式实践。
 在纯 C 中实现封装、继承、多态三大 OOP 特性,对标 Linux 内核与工业嵌入式实践。
 
 
 - [[source-c-oop]] — 原始资料摘要
 - [[source-c-oop]] — 原始资料摘要
-- `raw/Joplin/嵌入式+Linux/C语言OOP封装完整系列/c-oop.md` — 完整指南(含 C vs C++/Java 详细对比)
+- `raw/Joplin/嵌入式+Linux/C语言OOP封装完整系列/c-oop.md` — 完整指南(含可复制多文件 C 示例、编译命令与 C vs C++/Java 对比)

+ 16 - 0
X-Knowledge-Base/wiki/log.md

@@ -181,3 +181,19 @@ type: log
    - 完整代码示例与学习路径
    - 完整代码示例与学习路径
 
 
 - `lint`: 移动 c-oop.md 到 raw/,删除 wiki/c-oop.md,更新引用路径
 - `lint`: 移动 c-oop.md 到 raw/,删除 wiki/c-oop.md,更新引用路径
+
+## 2026-07-25
+
+- `wiki`: 完善 C 语言 OOP 指南示例与核验说明
+
+   更新页面:
+   - `raw/Joplin/嵌入式+Linux/C语言OOP封装完整系列/c-oop.md`
+   - `wiki/source-c-oop.md`
+   - `wiki/index.md`
+   - `wiki/log.md`
+
+   修订内容:
+   - 将 Student、RingBuffer、继承、多态、Stream、四层架构示例整理为按真实文件拆分的 `.h` / `.c` / `main.c` 代码块
+   - 为每组示例补充 `gcc -std=c11 -Wall -Wextra -Werror -pedantic` 编译命令和预期输出
+   - 修正不透明类型、结构体首成员、`container_of`、GNU C 扩展与 C11 可移植性相关说明
+   - 从 Markdown 代码块抽取成临时多文件项目,6 组示例均编译运行通过

+ 13 - 7
X-Knowledge-Base/wiki/source-c-oop.md

@@ -6,11 +6,12 @@ author: "500强11年嵌入式工程师"
 date: 2026-05-05
 date: 2026-05-05
 url: ""
 url: ""
 created: 2026-07-23
 created: 2026-07-23
+updated: 2026-07-25
 ---
 ---
 
 
 # C语言OOP封装完整系列 — 资料摘要
 # C语言OOP封装完整系列 — 资料摘要
 
 
-> 原始资料:`raw/Joplin/嵌入式+Linux/C语言OOP封装完整系列/`,包含 PDF 笔记、PPTX 和 DOCX 三份文件
+> 原始资料:`raw/Joplin/嵌入式+Linux/C语言OOP封装完整系列/`,包含课程资料与整理后的 `c-oop.md` 学习指南
 > 主题:用纯 C 语言实现面向对象编程的封装、继承、多态三大特性,最终构建嵌入式四层架构。
 > 主题:用纯 C 语言实现面向对象编程的封装、继承、多态三大特性,最终构建嵌入式四层架构。
 
 
 ## 核心脉络
 ## 核心脉络
@@ -50,6 +51,12 @@ created: 2026-07-23
 | 延迟绑定 | 运行时决定调用哪个函数 | 函数指针赋值 + 间接调用 |
 | 延迟绑定 | 运行时决定调用哪个函数 | 函数指针赋值 + 间接调用 |
 | 自动注册 | 模块无需修改主函数 | `__attribute__((section()))` + 链接脚本 |
 | 自动注册 | 模块无需修改主函数 | `__attribute__((section()))` + 链接脚本 |
 
 
+## 2026-07-25 修订要点
+
+- `c-oop.md` 中的 Student、RingBuffer、继承、多态、Stream、四层架构示例已改为按真实文件拆分的代码块(`.h` / `.c` / `main.c`),复制后可直接新建文件编译。
+- 示例均按 `gcc -std=c11 -Wall -Wextra -Werror -pedantic` 抽取编译并运行验证。
+- 修正不透明类型不能在外部栈上实例化、`strncpy` 需要手动补 `'\0'`、GNU C 扩展不可当作标准 C11 等容易误导的点。
+
 ## 四层架构
 ## 四层架构
 
 
 ```
 ```
@@ -70,15 +77,14 @@ created: 2026-07-23
 ## 关键宏
 ## 关键宏
 
 
 ```c
 ```c
-// 计算成员在结构体中的偏移量(字节数)
-#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
+#include <stddef.h>
 
 
-// 从成员指针反推结构体指针
-#define container_of(ptr, type, member) ({ \
-    void *__mptr = (void *)(ptr); \
-    ((type *)(__mptr - offsetof(type, member))); })
+#define container_of(ptr, type, member) \
+    ((type *)((char *)(ptr) - offsetof(type, member)))
 ```
 ```
 
 
+说明:用户态教学示例使用标准 C11 形式;Linux 内核版本会结合 GNU C 扩展做类型检查,并提供 `container_of_const()` 保留 `const` 限定。
+
 ## 参考关联
 ## 参考关联
 
 
 - `raw/Joplin/嵌入式+Linux/C语言OOP封装完整系列/c-oop.md` — 完整指南
 - `raw/Joplin/嵌入式+Linux/C语言OOP封装完整系列/c-oop.md` — 完整指南