目录
在C语言中,结构体(struct
)是一种自定义的数据类型,可以将多个不同类型的数据组合在一起。本文将介绍结构体的基本概念、变量创建与初始化,并重点解析结构体的内存对齐。此外,我们还将介绍与结构体相关的union
(联合体)和enum
(枚举)。
1. 结构体基础入门
struct 结构体标签 {
类型 成员1;
类型 成员2;
// ...
};
1.1 为什么需要结构体?
当我们要描述一个复杂对象时(如学生信息),单一数据类型无法满足需求。结构体可以将不同类型的数据打包成一个整体。
// 传统方式需要多个独立变量
int stu_id;
char stu_name[20];
float stu_score;
// 结构体解决方案
struct Student {
int id;
char name[20];
float score;
};
1.2 结构体类型定义
使用struct
关键字创建自定义类型:
struct 结构体标签 {
类型 成员1;
类型 成员2;
// ...
};
示例:通讯录联系人
struct Contact {
char name[30];
char phone[12];
int age;
char address[50];
};
2. 结构体变量操作
2.1 变量声明方式
// 方式1:声明时直接创建
struct Student {
// 成员列表
} stu1, stu2;
// 方式2:先定义类型再声明变量
struct Student stu3;
// 方式3:使用typedef简化
typedef struct {
// 成员列表
} Student;
Student stu4;
2.2 变量初始化
// 顺序初始化
struct Point {
int x;
int y;
};
struct Point p1 = {10, 20};
// C99指定初始化(可乱序)
struct Point p2 = {.y = 30, .x = 5};
// 结构体数组初始化
struct Student class[3] = {
{101, "Alice", 89.5},
{102, "Bob", 92.0},
{103, "Charlie", 78.5}
};
3. 结构体的内存对齐
C语言的结构体在内存中存储时,会遵循内存对齐规则,以提高CPU的访问效率。
3.1 为什么需要内存对齐?
-
硬件访问效率:CPU读取内存时以4/8字节为单位
-
平台移植性:不同系统可能有不同的对齐要求
3.2 内存对齐规则
通过实例分析内存布局:
struct Example {
char a; // 1字节
int b; // 4字节
char c; // 1字节
};
未对齐状态猜想:
[char][ int ][char] → 1+4+1=6字节
实际内存布局(假设4字节对齐):
Offset 0: [char a][填充3字节]
Offset 4: [ int b ]
Offset 8: [char c][填充3字节]
总大小:12字节
3.2.1 内存对齐规则详解
-
对齐原则
-
每个成员变量的偏移量必须是该成员**自身类型大小或默认对齐数(通常是编译器默认的对齐方式)的整数倍。
-
结构体的总大小必须是最大对齐数的整数倍。
-
-
示例解析
-
char a;
占 1 字节,但int b;
需要4字节对齐,因此a
后面会填充3
个字节。 -
int b;
按 4 字节对齐,占 4 字节。 -
char c;
需要 1 字节对齐,占 1 字节。 -
结构体总大小需要是最大对齐数
4
的整数倍,因此最终大小是12
字节。
-
0 1 2 3 4 5 6 7 8 9 10 11
+---+---+---+---+---+---+---+---+---+---+---+---+
| a | empty | b | c | empty |
+---+---+---+---+---+---+---+---+---+---+---+---+
最终结构体大小为 12
字节,而不是 6 字节。
3.3 优化技巧
调整成员顺序可节省空间:
// 优化前:12字节
struct S1 {
char a;
int b;
char c;
};
// 优化后:8字节
struct S2 {
int b;
char a;
char c;
};
优化前:
0 1 2 3 4 5 6 7 8 9 10 11
+---+---+---+---+---+---+---+---+---+---+---+---+
| a | empty | b | c | empty |
+---+---+---+---+---+---+---+---+---+---+---+---+
优化后:
0 1 2 3 4 5 6 7 8
+---+---+---+---+---+---+---+---+
| b | a | c | empty |
+---+---+---+---+---+---+---+---+
3.4 查看与修改对齐
查看结构体大小
printf("结构体大小:%zu\n", sizeof(struct Example));
修改对齐方式
#pragma pack(1) // 设置1字节对齐
struct PackedStruct {
// 成员定义
};
#pragma pack() // 恢复默认对齐
offsetof
宏查看成员偏移量
// 查看成员偏移示例
#include <stddef.h>
printf("b的偏移量:%zu\n", offsetof(struct Example, b));
4. 联合体(Union)
union
(联合体)与struct
类似,但所有成员共享同一块内存,因此联合体的大小等于其最大成员的大小。
4.1 定义联合体
union Data {
int i;
float f;
char str[20];
};
4.2 联合体的内存布局
0 1 2 3 4 5 ... 19
+---+---+---+---+---+---+---+---+
| str (20B) |
+---+---+---+---+---+---+---+---+
整个 union
的大小是 20
字节,因为 str[20]
是最大成员。
4.3 用 union 判断字节序
#include <stdio.h>
union EndianTest {
int i;
char c;
};
int main() {
union EndianTest test;
test.i = 1;
if (test.c == 1) {
printf("小端序\n");
} else {
printf("大端序\n");
}
return 0;
}
如果输出 小端序
,说明最低字节存储在最低地址(即 Little Endian
方式)。
5. 枚举(Enum)
enum
(枚举)用于定义一组具名整数常量,提升代码可读性。
定义枚举类型:
enum Color {
RED, // 默认值 0
GREEN, // 默认值 1
BLUE // 默认值 2
};
可以手动指定值:
enum Color {
RED = 1,
GREEN = 5,
BLUE = 10
};
使用枚举:
enum Color c = GREEN;
printf("Color: %d\n", c); // 输出 5
6. 总结
-
struct
组合不同类型数据,支持内存对齐。 -
union
共享同一块内存,仅存储一个成员的值,节省内存但有数据覆盖风险。 -
enum
定义具名整数常量,提高可读性。 -
结构体优化成员顺序可以减少内存浪费。
-
使用
union
可以判断系统的字节序。