|
@@ -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 底层原理
|