预编译指令:
#pragma anon_unions
这条指令就是告诉编译器,支持匿名结构体/联合体。
为什么要加这个预编译指令:
很多时候keil的编译器标准都是C99,而匿名联合体是C11标准引入的特性。
- 匿名结构体/联合体 是 C11标准(ISO/IEC 9899:2011)引入的特性
- ARMCC(AC5) 基于 C99标准,默认不支持 C11 特性。
- GCC/Clang 默认支持 C11 及扩展语法,因此无需特殊指令即可使用匿名结构体/联合体。
什么是匿名结构体/共用体
在另一个结构中声明结构变量,而无需为其命名的嵌套结构称为匿名结构。并且可以像访问包含结构中的成员一样访问匿名结构的成员。
匿名结构体/联合体指的是没有命名的结构,因为没有对应的名字,所以也不会直接创建这个对象或者变量,一般都是在嵌套结构中使用。
1. 匿名结构体/共用体的定义
匿名结构体:在父结构体中直接嵌套一个未命名的结构体,其成员视为父结构体的成员。
匿名共用体:在父结构体/共用体中嵌套一个未命名的共用体,其成员共享内存空间,可直接访问。
// 匿名结构体 + 匿名共用体示例
typedef struct {
int id;
union { // 匿名共用体
struct { // 匿名结构体
float x, y, z; // 可直接访问 x, y, z
};
float position[3]; // 也可直接访问 position[0], position[1], position[2]
};
} GameObject;
// 使用方式:
GameObject obj;
obj.x = 1.0f; // 直接访问匿名结构体成员
obj.position[1] = 2.0f; // 直接访问匿名共用体成员
2. 核心作用:简化代码访问
匿名结构体/共用体的核心价值是 消除中间访问层级,使代码更简洁直观。常用于以下场景:
场景 1:寄存器位域映射
在嵌入式开发中,通过匿名结构体/共用体直接访问寄存器的位域:
typedef struct {
union {
uint32_t reg; // 整个32位寄存器
struct { // 匿名结构体按位拆分
uint32_t bit0 :1;
uint32_t bit1 :1;
uint32_t bit2_5 :4;
// ...
};
};
} ControlRegister;
// 使用方式:
ControlRegister cr;
cr.reg = 0x12345678; // 直接写整个寄存器
cr.bit0 = 1; // 直接操作某一位
场景 2:协议数据解析
在通信协议中,匿名共用体可实现同一段内存的多种解释方式:
typedef struct {
uint8_t type;
union {
struct { // 类型1的数据格式
uint16_t speed;
uint32_t timestamp;
};
struct { // 类型2的数据格式
float temperature;
float humidity;
};
uint8_t raw_data[8]; // 原始字节流
};
} SensorPacket;
// 使用方式:
SensorPacket packet;
if (packet.type == 1) {
printf("Speed: %d\n", packet.speed); // 直接访问
} else if (packet.type == 2) {
printf("Temp: %.1f\n", packet.temperature);
}
3. 与普通结构体/共用体的区别
特性 | 普通结构体/共用体 | 匿名结构体/共用体 |
---|---|---|
成员访问方式 | 需通过中间名称(如 obj.inner.x ) | 直接访问(如 obj.x ) |
内存布局 | 独立命名空间,按定义顺序分配内存 | 与父结构体共享内存空间 |
适用场景 | 需要明确层级关系的复杂数据 | 简化访问、多视角解释同一段内存 |