第九章:结构体与联合体
9.1 结构体定义与成员访问
结构体定义
结构体(struct
)是用户自定义的复合数据类型,用于将不同类型的数据组合成一个整体。
语法:
struct 结构体标签 {
数据类型 成员1;
数据类型 成员2;
// ...
};
示例:定义学生信息结构体
struct Student {
int id;
char name[20];
float score;
};
声明与初始化
// 声明结构体变量
struct Student stu1;
// 初始化(按成员顺序)
struct Student stu2 = {1001, "Alice", 92.5};
// 指定成员初始化(C99支持)
struct Student stu3 = {.id = 1002, .name = "Bob", .score = 85.0};
成员访问
• 使用 .
运算符访问结构体成员:
stu1.id = 1003;
strcpy(stu1.name, "Charlie");
stu1.score = 88.5;
printf("ID: %d, Name: %s\n", stu1.id, stu1.name);
内存分配
• 结构体变量占用的内存大小等于所有成员大小之和(考虑内存对齐,见9.3节)。
9.2 结构体数组与指针
结构体数组
struct Student class[3] = {
{1001, "Alice", 90.5},
{1002, "Bob", 85.0},
{1003, "Charlie", 92.0}
};
// 遍历数组
for (int i = 0; i < 3; i++) {
printf("Student %d: %s\n", class[i].id, class[i].name);
}
结构体指针
struct Student *p = &stu1;
// 通过指针访问成员
printf("ID: %d\n", (*p).id);
// 等价简写(箭头运算符)
printf("Name: %s\n", p->name);
指向结构体数组的指针
struct Student *ptr = class;
for (int i = 0; i < 3; i++) {
printf("Score: %.1f\n", ptr->score);
ptr++; // 移动到下一个结构体元素
}
9.3 结构体嵌套与对齐原则
嵌套结构体
struct Date {
int year;
int month;
int day;
};
struct Employee {
int id;
char name[20];
struct Date hire_date; // 嵌套结构体
};
// 初始化嵌套结构体
struct Employee emp = {
101,
"Dave",
{2020, 7, 15}
};
// 访问嵌套成员
printf("Hire Year: %d\n", emp.hire_date.year);
内存对齐原则
• 对齐规则:
- 结构体成员的偏移地址必须是其自身大小的整数倍。
- 结构体总大小必须是最大成员大小的整数倍。
示例:分析结构体大小
struct Example1 {
char a; // 1字节
int b; // 4字节(偏移需为4的倍数)
short c; // 2字节
};
// 内存布局: |a|---|bbbb|cc--| → 总大小 = 12 字节
struct Example2 {
int b; // 4字节
char a; // 1字节
short c; // 2字节
};
// 内存布局: |bbbb|a|cc| → 总大小 = 8 字节
手动调整对齐
• 使用 #pragma pack(n)
修改默认对齐系数(需谨慎):
#pragma pack(1) // 按1字节对齐
struct PackedStruct {
char a;
int b;
short c;
};
// 总大小 = 1 + 4 + 2 = 7 字节
#pragma pack() // 恢复默认对齐
9.4 联合体(union)与位域
联合体定义
联合体(union
)的所有成员共享同一块内存空间,同一时间只能存储一个成员的值。
语法:
union 联合体标签 {
数据类型 成员1;
数据类型 成员2;
// ...
};
示例:存储不同类型数据
union Data {
int i;
float f;
char str[4];
};
union Data data;
data.i = 42;
printf("Integer: %d\n", data.i);
data.f = 3.14;
printf("Float: %.2f\n", data.f); // 此时data.i的值被覆盖
内存共享特性
• 联合体大小等于最大成员的大小(需考虑对齐)。
• 应用场景:节省内存、实现“变体”数据类型。
位域(Bit Fields)
用于将结构体成员按二进制位分配内存,适用于硬件寄存器操作或压缩存储。
示例:定义权限位域
struct Permissions {
unsigned int read : 1; // 1位(0或1)
unsigned int write : 1;
unsigned int execute : 1;
unsigned int reserved : 5; // 5位保留位
};
struct Permissions perm;
perm.read = 1;
perm.write = 0;
printf("Size: %zu bytes\n", sizeof(perm)); // 输出:4字节(int对齐)
9.5 枚举类型(enum)
枚举定义
枚举(enum
)用于定义一组命名的整数常量,增强代码可读性。
语法:
enum 枚举标签 {
常量名1,
常量名2,
// ...
};
示例:定义状态码
enum HttpStatus {
OK = 200,
NOT_FOUND = 404,
SERVER_ERROR = 500
};
enum HttpStatus res = OK;
if (res == NOT_FOUND) {
printf("Error: 404\n");
}
枚举特性:
常量默认从0开始递增(可显式赋值):
enum Week { Sun = 7, Mon = 1, Tue, Wed }; // Tue=2, Wed=3
```
2. 枚举变量本质是整型(`int`),可参与算术运算。
3. 作用域规则与结构体/联合体类似。
**应用场景**:
• 状态码、选项标志、有限集合(如星期、月份)。
---
### **总结**
1. **结构体**:
• 组合异构数据,支持嵌套与指针操作。
• 内存对齐影响空间效率,需根据需求调整成员顺序。
2. **联合体**:
• 共享内存,适用于类型转换或节省空间。
• 使用需谨慎,避免意外覆盖数据。
3. **枚举**:
• 提升代码可读性,替代魔法数字。
4. **位域**:
• 精细控制内存布局,适用于底层硬件编程。
**注意事项**:
• 结构体作为函数参数时建议传递指针以减少拷贝开销。
• 联合体不具备类型安全性,需自行管理当前有效成员。
• 枚举常量在全局作用域内可见,避免命名冲突。