0.3 C 语言关键知识点
2026/7/19大约 4 分钟预备知识预备知识C语言指针
0.3 C 语言关键知识点
📚 本节导读
学习时长:约 20 分钟
难度级别:⭐⭐☆☆☆
前置知识:C 语言基础语法
🎯 学习目标
- 复习阅读 OneOS 源码必需的 C 语言知识点
- 理解指针、结构体、函数指针在 RTOS 中的实际应用
- 掌握条件编译和 volatile 关键字的用法
一、指针(Pointer)
1.1 基本概念
指针存储的是内存地址,是 C 语言最强大的特性之一。OneOS 源码中大量使用指针。
int value = 42; // 普通变量
int *ptr = &value; // 指针变量,存储 value 的地址
*ptr = 100; // 通过指针修改 value
printf("%d", value); // 输出 1001.2 RTOS 中的指针应用
/* 任务创建时传递参数(OneOS 源码模式) */
void my_task(void *arg)
{
int *param = (int *)arg; // void* 转换为具体类型
int value = *param;
}
int main(void)
{
int param = 42;
os_task_create(&task, "my_task", my_task,
¶m, ...); // 传递参数的地址
// ^^^^^^ void * 类型
}1.3 函数指针
函数指针指向函数代码的入口地址,是 RTOS 回调机制的基础。
/* 函数指针类型定义 */
typedef void (*task_entry_t)(void *arg);
/* 函数指针作为参数(OneOS 任务创建的实际模式) */
void create_task(task_entry_t entry, void *arg)
{
// 保存 entry 指针,调度器在适当时机调用
entry(arg); // 通过函数指针调用任务函数
}二、结构体(Struct)
2.1 基本用法
结构体将相关数据组合在一起,是 RTOS 对象建模的核心工具。
/* OneOS 任务控制块(简化版) */
struct os_task
{
char name[16]; // 任务名
void *sp; // 栈指针
uint8_t priority; // 优先级
uint32_t state; // 任务状态
void *stack; // 栈起始地址
uint32_t stack_size; // 栈大小
};2.2 结构体指针
OneOS API 大量使用结构体指针作为句柄:
/* 通过指针操作结构体 */
os_task_t *task; // 结构体指针
os_task_create(&task, ...); // 二重指针,用于返回任务句柄
os_task_set_priority(task, 10); // 通过句柄操作任务2.3 结构体中的函数指针
这是 OneOS 驱动框架的核心设计模式 — 操作接口表:
/* 设备操作接口(驱动框架核心) */
struct os_device_ops
{
int (*init) (os_device_t *dev);
int (*open) (os_device_t *dev);
int (*close) (os_device_t *dev);
int (*read) (os_device_t *dev, void *buf, int len);
int (*write) (os_device_t *dev, const void *buf, int len);
int (*control)(os_device_t *dev, int cmd, void *arg);
};三、条件编译
3.1 基本用法
条件编译根据宏定义选择性地编译代码,OneOS 通过 Kconfig 生成的 os_cfg.h 来控制。
/* 根据配置决定是否编译某功能 */
#ifdef OS_USING_SEM
// 编译信号量相关代码
os_sem_create(&sem, "my_sem", 1);
#endif
#if OS_TASK_PRIORITY_MAX > 32
// 优先级较多时的优化路径
#else
// 优先级较少时的轻量路径
#endif3.2 在 OneOS 中的实际应用
/* 每个模块的 Kconfig 配置最终体现为条件编译 */
#ifdef OS_USING_CONSOLE
os_console_init(); // 启用控制台才编译
#endif
#ifdef OS_USING_NET
#include <net/socket.h> // 启用网络才包含
#endif四、volatile 关键字
4.1 作用
告诉编译器「此变量可能被意外修改」,禁止编译器做优化假设。
4.2 RTOS 中的典型场景
/* 场景1:ISR 与任务共享的变量 */
volatile int flag = 0; // 禁止编译器优化掉重复读取
void isr_handler(void)
{
flag = 1; // ISR 中修改
}
void task(void)
{
while (flag == 0) {
// 编译器不会优化成死循环
// 每次循环都从内存重新读取 flag
}
// flag 被 ISR 置位后,继续执行
}
/* 场景2:内存映射寄存器 */
#define GPIOA_ODR (*(volatile uint32_t *)0x40020014)
// 确保每次读写都真正访问硬件寄存器五、其他重要知识点
5.1 static 关键字
| 用法 | 含义 |
|---|---|
static int x; (函数内) | 静态局部变量,生命周期为整个程序,仅初始化一次 |
static int x; (文件内) | 文件作用域,外部文件不可见 |
static void func() | 函数仅在本文件可见 |
OneOS 内部辅助函数大量使用 static 限制作用域,避免命名冲突。
5.2 typedef
/* OneOS 中的类型别名 */
typedef unsigned int os_uint32_t;
typedef struct os_task os_task_t; // 简化结构体使用
typedef void (*os_task_entry_t)(void *arg); // 函数指针类型5.3 链表
OneOS 内核大量使用双向链表管理对象(任务、定时器、设备等):
/* OneOS 链表节点 */
typedef struct os_list_node
{
struct os_list_node *next;
struct os_list_node *prev;
} os_list_node_t;
/* 通过链表节点反推包含它的结构体 */
#define os_container_of(ptr, type, member) \
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))📝 本节小结
- 指针:RTOS 参数传递和回调机制的基础,
void*是通用句柄 - 结构体 + 函数指针:构成驱动框架的操作接口表(
os_device_ops) - 条件编译:根据 Kconfig 配置按需编译,
#ifdef OS_USING_XXX - volatile:ISR 共享变量和硬件寄存器必须使用
- 链表:内核管理对象的核心数据结构,掌握
os_container_of宏