要通过 typedef struct xxx *handle_t 这种语法定义的句柄(handle)操作结构体成员,核心是理解:句柄本质是结构体指针,操作成员的语法与普通结构体指针完全一致(用 -> 运算符)。但需要注意:这种封装方式通常要求结构体的完整定义可见(否则无法直接访问成员)。
具体步骤与示例
1. 先定义完整的结构体(关键前提)
typedef struct i2s_channel_obj_t *i2s_chan_handle_t; 只是声明了结构体指针的别名,但要访问成员,必须先有结构体的完整定义(包含具体成员)。通常结构体定义会放在 .c 文件或头文件中(若允许外部访问)。
例如:
// 完整的结构体定义(包含成员)
struct i2s_channel_obj_t {
uint32_t reg_base; // I2S寄存器基地址
uint8_t channel_num; // 通道号
bool is_tx; // 是否为发送通道(true=发送,false=接收)
uint32_t baud_rate; // 波特率
};
// 为结构体指针定义别名(句柄)
typedef struct i2s_channel_obj_t *i2s_chan_handle_t;
2. 通过句柄(指针)操作成员
i2s_chan_handle_t 本质是 struct i2s_channel_obj_t *(结构体指针),因此可以通过 -> 运算符访问结构体成员,语法与普通指针完全一致。
示例:初始化句柄并访问成员
#include <stdlib.h> // 用于malloc
// 1. 创建句柄并分配内存(句柄指向结构体实例)
i2s_chan_handle_t i2s_chan = malloc(sizeof(struct i2s_channel_obj_t));
if (i2s_chan == NULL) {
// 内存分配失败处理
return;
}
// 2. 通过句柄(指针)访问并修改成员
i2s_chan->reg_base = 0x40003000; // 设置寄存器基地址
i2s_chan->channel_num = 1; // 通道号设为1
i2s_chan->is_tx = true; // 配置为发送通道
i2s_chan->baud_rate = 44100; // 波特率设为44.1kHz
// 3. 读取成员值
printf("当前通道号:%d\n", i2s_chan->channel_num); // 输出:1
printf("是否为发送通道:%s\n", i2s_chan->is_tx ? "是" : "否"); // 输出:是
// 4. 使用完释放内存
free(i2s_chan);
i2s_chan = NULL; // 避免野指针
关键注意事项
1. 结构体定义必须可见
如果结构体 struct i2s_channel_obj_t 只做了“前向声明”(没有完整定义,如下),编译器无法知道成员信息,直接访问成员会报错。
// 仅前向声明(不完整类型)
struct i2s_channel_obj_t;
typedef struct i2s_channel_obj_t *i2s_chan_handle_t;
// 此时访问成员会报错:“不完全类型的指针不可访问成员”
i2s_chan_handle_t chan;
chan->reg_base = 0x40003000; // 编译错误!
解决办法:确保在访问成员的代码文件中,能看到结构体的完整定义(包含成员列表)。
2. 封装思想:通常不建议直接访问成员
typedef 结构体指针为句柄的设计,核心目的是封装结构体细节(隐藏成员),让用户通过API函数操作,而非直接修改成员。例如:
// 提供API函数(而非让用户直接改成员)
void i2s_chan_set_baud_rate(i2s_chan_handle_t chan, uint32_t baud) {
if (chan != NULL) {
chan->baud_rate = baud; // 内部访问成员,外部只需调用函数
}
}
// 用户使用方式(无需知道结构体成员)
i2s_chan_handle_t chan = i2s_chan_create(); // 创建句柄的API
i2s_chan_set_baud_rate(chan, 44100); // 调用API设置波特率
这种方式的优势:
隐藏实现细节(成员可能随版本变化,用户无需关心);
避免误操作(如API内部可加参数校验,直接改成员可能越界)。
总结
通过 typedef struct xxx *handle_t 定义的句柄操作结构体成员,语法上与普通结构体指针一致(用 -> 运算符),但前提是结构体必须有完整定义。不过从封装设计出发,更推荐通过配套的API函数操作,而非直接访问成员,以保证代码的安全性和可维护性。
————————————————
版权声明:本文为CSDN博主「Shylock_Mister」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Shylock_Mister/article/details/153841966
|
|