C语言自定义类型:结构体完全指南
结构体是C语言中最重要的自定义类型,允许将不同类型的数据组合成一个整体。本文将系统讲解结构体的声明、初始化、成员访问、内存对齐规则、传参方式以及位段的应用,帮助读者掌握结构体的核心用法与底层细节。
目录
一、结构体类型的声明
结构体是一组值的集合,每个成员可以是不同类型。
c
struct Stu {
char name[20];
int age;
char sex[5];
char id[20];
}; // 分号不能丢
1.1 特殊的声明(匿名结构体)
声明时可以省略结构体标签(tag),但此类结构体只能使用一次:
c
struct {
int a;
char b;
float c;
} x; // 直接定义变量
注意:两个匿名结构体即使成员相同,也被视为不同类型,不能相互赋值。
1.2 结构的自引用
在链表的节点中,需要包含指向自身类型的指针:
c
struct Node {
int data;
struct Node* next; // 正确,指针大小固定
};
错误示例 :struct Node next; 会导致无限递归,无法计算大小。
使用 typedef 时需注意:
c
typedef struct Node {
int data;
struct Node* next; // 必须用 struct Node,不能用 Node
} Node;
二、结构体变量的创建和初始化
c
struct Stu s1 = {"张三", 20, "男", "20230818001"}; // 顺序初始化
struct Stu s2 = {.age = 18, .name = "李四", .id = "20230818002", .sex = "女"}; // 指定顺序
三、结构成员访问操作符
- 点操作符
.:结构体变量直接访问成员,如s1.age。 - 箭头操作符
->:结构体指针访问成员,如ps->age。
四、结构体内存对齐
这是计算结构体大小的核心考点。

4.1 对齐规则
- 第一个成员:放在偏移量为0的地址处。
- 其他成员 :对齐到对齐数 的整数倍地址。
对齐数 =min(编译器默认对齐数, 成员大小)。
VS默认对齐数为8,Linux默认为4。 - 结构体总大小 :必须是最大对齐数的整数倍。
- 嵌套结构体:嵌套的结构体对齐到自身最大对齐数的整数倍,整体大小是所有最大对齐数的整数倍。
4.2 示例分析
c
struct S1 {
char c1; // 1字节,偏移0
int i; // 4字节,对齐数4,偏移从4开始
char c2; // 1字节,对齐数1,偏移8
}; // 最大对齐数4,总大小须为4的倍数 → 12字节
struct S2 {
char c1; // 偏移0
char c2; // 偏移1
int i; // 对齐数4,偏移从4开始
}; // 总大小8字节
节省空间技巧:将占用小的成员集中在一起。
4.3 修改默认对齐数
使用 #pragma pack(1) 设置对齐数为1,可取消对齐(紧凑存储)。
c
#pragma pack(1)
struct S { char c1; int i; char c2; };
#pragma pack()
// sizeof(struct S) = 1+4+1 = 6
五、结构体传参
c
void print1(struct S s) { ... } // 传值:拷贝整个结构体,效率低
void print2(struct S* ps) { ... } // 传址:只传指针,效率高
结论:结构体传参应优先传递指针(地址),避免压栈开销。
六、结构体实现位段
6.1 什么是位段
位段允许指定成员占用的比特位数 ,用于节省内存。
声明规则:成员类型为 int、unsigned int、signed int 或 char,成员名后跟冒号和数字。
c
struct A {
int _a : 2; // 占2 bit
int _b : 5; // 占5 bit
int _c : 10;
int _d : 30;
};
sizeof(struct A) 在VS中通常为8字节(因4+4字节,成员共47bit,需两个int)。
6.2 位段的内存分配
- 位段按需以
int(4字节)或char(1字节)为单位开辟空间。 - 位段不跨平台,各编译器实现可能不同(如顺序、舍弃规则未定义)。
示例(VS2013下):
c
struct S {
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
struct S s = {0};
s.a = 10; // 10的二进制1010,但只取低3位 → 010
s.b = 12; // 1100
s.c = 3; // 00011
s.d = 4; // 0100
内存布局(从左到右分配,低地址在右)需依编译器实际测试。
6.3 位段的跨平台问题
int位段被当作有符号还是无符号不确定。- 最大位数不确定(16位机器最大16)。
- 成员分配方向(从左向右或从右向左)未定义。
- 剩余位是否利用不确定。
建议:位段适用于对内存要求极高的场景(如网络协议),但可移植程序中应避免。
6.4 位段的应用
IP数据报头部等网络协议字段常使用位段表示,以节省带宽。
6.5 位段使用的注意事项
不能对位段成员取地址 (因为其可能跨越字节边界)。无法用 scanf 直接输入,需借助临时变量:
c
int b;
scanf("%d", &b);
sa._b = b;
总结:结构体是组织不同类型数据的容器,其内存对齐规则保证了访问效率,但可能产生空间浪费,可通过调整成员顺序或修改对齐数优化。位段以bit为单位分配内存,适合表示标志字段,但存在跨平台问题。结构体传参应使用指针以提升性能。掌握结构体是学习链表、树等数据结构的基础。