联合体的所有成员共享同一块内存空间,同一时刻只有一个成员有效。联合体的大小等于最大成员的大小(含对齐填充)。
枚举用于定义一组命名的整型常量,使代码更具可读性。枚举值从 0 开始递增,也可以手动指定值。
| 语法 | 说明 |
|---|---|
union Tag { ... }; |
联合体声明 |
union Tag u = {.x = 1}; |
C99 指定初始化器 |
u.member |
访问当前有效成员 |
enum Tag { A, B, C }; |
枚举声明 |
enum Tag val = A; |
枚举赋值 |
enum Tag { A=10, B, C=20 }; |
手动指定枚举值 |
// 13_union_basics.c
#include <stdio.h>
#include <stddef.h>
union Data {
int i;
float f;
char str[4];
};
// 大小端检测联合体
union EndianCheck {
unsigned int val;
unsigned char bytes[4];
};
int main(void)
{
// 所有成员共享同一块内存
union Data d;
d.i = 0x41424344;
printf("d.i = 0x%X\n", d.i);
printf("d.f = %f\n", d.f); // 读取浮点(结果无意义)
printf("d.str = %.4s\n", d.str); // 读取字符串
printf("\nsizeof(union Data) = %zu\n", sizeof(union Data));
printf("sizeof(int) = %zu, sizeof(float) = %zu\n", sizeof(int), sizeof(float));
// 大小端检测
union EndianCheck ec;
ec.val = 0x01020304;
printf("\nEndian check for 0x01020304:\n");
printf(" bytes[0]=0x%02X, bytes[1]=0x%02X, bytes[2]=0x%02X, bytes[3]=0x%02X\n",
ec.bytes[0], ec.bytes[1], ec.bytes[2], ec.bytes[3]);
if (ec.bytes[0] == 0x01) {
printf(" -> Big Endian\n");
} else if (ec.bytes[0] == 0x04) {
printf(" -> Little Endian\n");
} else {
printf(" -> Middle Endian (PDP)\n");
}
return 0;
}
编译运行:
gcc -Wall -Wextra -std=c99 -o 13_union_basics 13_union_basics.c
./13_union_basics
// 13_union_reg.c
#include <stdio.h>
// 嵌入式常见:寄存器的位域视图与整型视图共享内存
union Reg {
unsigned int raw;
struct {
unsigned int enable : 1; // bit 0
unsigned int mode : 3; // bit 1-3
unsigned int irq_en : 1; // bit 4
unsigned int reserved: 27; // bit 5-31
} bits;
};
int main(void)
{
union Reg reg;
reg.raw = 0;
// 通过位域设置
reg.bits.enable = 1;
reg.bits.mode = 5; // 101b
reg.bits.irq_en = 1;
printf("reg.raw = 0x%08X\n", reg.raw);
printf(" enable = %u\n", reg.bits.enable);
printf(" mode = %u\n", reg.bits.mode);
printf(" irq_en = %u\n", reg.bits.irq_en);
// 也可以直接写整型
reg.raw = 0x0000001B;
printf("\nAfter raw write 0x1B:\n");
printf(" enable = %u\n", reg.bits.enable);
printf(" mode = %u\n", reg.bits.mode);
printf(" irq_en = %u\n", reg.bits.irq_en);
return 0;
}
编译运行:
gcc -Wall -Wextra -std=c99 -o 13_union_reg 13_union_reg.c
./13_union_reg
// 13_enum_state.c
#include <stdio.h>
// 电梯状态机
enum ElevatorState {
STATE_IDLE = 0,
STATE_MOVING_UP = 1,
STATE_MOVING_DOWN= 2,
STATE_DOOR_OPEN = 3,
STATE_ERROR = 4
};
// 事件
enum ElevatorEvent {
EVT_CALL_UP,
EVT_CALL_DOWN,
EVT_ARRIVE_FLOOR,
EVT_OPEN_DOOR,
EVT_CLOSE_DOOR,
EVT_FAULT
};
const char *state_name(enum ElevatorState s)
{
switch (s) {
case STATE_IDLE: return "IDLE";
case STATE_MOVING_UP: return "MOVING_UP";
case STATE_MOVING_DOWN: return "MOVING_DOWN";
case STATE_DOOR_OPEN: return "DOOR_OPEN";
case STATE_ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
enum ElevatorState next_state(enum ElevatorState current, enum ElevatorEvent evt)
{
switch (current) {
case STATE_IDLE:
if (evt == EVT_CALL_UP) return STATE_MOVING_UP;
if (evt == EVT_CALL_DOWN) return STATE_MOVING_DOWN;
if (evt == EVT_FAULT) return STATE_ERROR;
break;
case STATE_MOVING_UP:
case STATE_MOVING_DOWN:
if (evt == EVT_ARRIVE_FLOOR) return STATE_DOOR_OPEN;
if (evt == EVT_FAULT) return STATE_ERROR;
break;
case STATE_DOOR_OPEN:
if (evt == EVT_CLOSE_DOOR) return STATE_IDLE;
break;
case STATE_ERROR:
break;
}
return current;
}
int main(void)
{
enum ElevatorState state = STATE_IDLE;
enum ElevatorEvent events[] = {
EVT_CALL_UP, EVT_ARRIVE_FLOOR, EVT_CLOSE_DOOR,
EVT_CALL_DOWN, EVT_FAULT
};
int n = sizeof(events) / sizeof(events[0]);
printf("Start: %s\n", state_name(state));
for (int i = 0; i < n; i++) {
state = next_state(state, events[i]);
printf(" Event %d -> State: %s\n", i + 1, state_name(state));
}
return 0;
}
编译运行:
gcc -Wall -Wextra -std=c99 -o 13_enum_state 13_enum_state.c
./13_enum_state
union 同时只读一个成员:写入 A 后读取 B 是未定义行为(类型双关除外,如通过 unsigned char* 逐字节读取)。
union 大小含对齐填充:sizeof(union) 是最大成员大小向上对齐到最大对齐值的结果。
enum 本质是 int:C 语言不检查 enum 值的范围,可以赋任意整数给 enum 变量。
enum 的值是编译期常量:不能用 scanf 读取 enum(要读 int 再赋值)。
命名冲突:enum/union 的标签名在同一作用域不能与变量名冲突。
Q1:union 和 struct 的区别?
struct 所有成员各自占用独立内存;union 所有成员共享同一块内存,大小等于最大成员。
Q2:如何用 union 检测大小端?
将整数写入 union 的
unsigned int成员,再通过unsigned char数组读取最低字节。最低地址存0x01是大端,存0x04是小端。
Q3:enum 和 #define 有什么区别?
- enum 是编译期类型,调试器可以打印名称;#define 是预处理文本替换。
- enum 自动编号,不需要手动维护值。
- enum 有作用域限制(在函数内声明只在函数内可见),#define 从定义点到文件末尾。
Q4:union 在嵌入式中有什么用途?
解析寄存器:同一地址既可按位域读取各标志位,也可按整型批量读写。也用于类型双关(type punning)在协议解析中。
Q5:C 语言中 enum 的值可以重复吗?
可以。不同枚举成员可以有相同值,编译器不报错。但同一枚举类型中重复值可能导致逻辑错误。