1. 结构体基本概念
结构体是将彼此相关的,类型不同的数据组合在一起的构造数据类型,它是由若干成员组成的,每个成员的数据类型可以是基本数据类型,也可以是构造类型。在使用某个结构之前必须先声明它,即根据需要构造它。
2. 结构体声明与定义
2.1 基本声明方式
struct 结构体名 {
数据类型 成员1;
数据类型 成员2;
// ...
};
示例:
struct Student {
char name[20];
int age;
float score;
};
2.2 定义结构体变量
// 方式1:先声明后定义
struct Student stu1;
// 方式2:声明时直接定义变量
struct Student {
char name[20];
int age;
} stu2, stu3;
// 方式3:使用typedef创建别名
typedef struct {
char name[20];
int age;
} Student;
Student stu4;
3. 结构体初始化
3.1 初始化方式
// 方式1:按声明顺序初始化
struct Student stu1 = {"张三", 20, 90.5};
// 方式2:指定成员初始化(C99支持)
struct Student stu2 = {
.name = "李四",
.age = 21,
.score = 88.5
};
// 方式3:部分初始化
struct Student stu3 = {"王五"}; // age和score初始化为0
4. 结构体成员访问
4.1 普通变量访问
使用.
运算符:
struct Student stu;
strcpy(stu.name, "赵六");
stu.age = 22;
stu.score = 95.0;
4.2 指针访问
使用->
运算符:
struct Student *pStu = &stu;
pStu->age = 23; // 等价于 (*pStu).age = 23;
5. 结构体大小与内存对齐
5.1 sizeof运算符
printf("结构体大小:%zu\n", sizeof(struct Student));
5.2 内存对齐规则
-
结构体第一个成员的偏移量为0
-
每个成员按其类型大小和对齐参数的较小值对齐
-
结构体总大小为最大对齐参数的整数倍
-
可通过
#pragma pack(n)
修改对齐参数
示例:
struct Example {
char a; // 1字节
int b; // 通常4字节(对齐到4的倍数)
double c; // 8字节
};
// 在64位系统上可能占用16字节(1+3填充+4+8)
6. 结构体数组
6.1 定义与初始化
struct Student class[3] = {
{"张三", 20, 90.5},
{"李四", 21, 88.5},
{"王五", 22, 92.0}
};
6.2 访问数组成员
class[1].age = 23; // 修改第二个学生的年龄
7. 结构体嵌套
7.1 嵌套结构体
struct Date {
int year;
int month;
int day;
};
struct Employee {
char name[20];
struct Date birthday;
float salary;
};
7.2 访问嵌套成员
struct Employee emp;
emp.birthday.year = 1990;
8. 结构体与函数
8.1 结构体作为函数参数
void printStudent(struct Student stu) {
printf("姓名:%s\n年龄:%d\n分数:%.1f\n",
stu.name, stu.age, stu.score);
}
8.2 结构体指针作为参数(更高效)
void modifyStudent(struct Student *pStu) {
pStu->age += 1;
}
8.3 返回结构体
struct Student createStudent() {
struct Student stu = {"新学生", 20, 80.0};
return stu;
}
9. 结构体与指针
9.1 结构体指针
struct Student *pStu = malloc(sizeof(struct Student));
if (pStu != NULL) {
strcpy(pStu->name, "动态分配");
pStu->age = 25;
free(pStu); // 记得释放内存
}
9.2 结构体包含指针
struct String {
char *data;
int length;
};
struct String str;
str.data = malloc(100);
str.length = 0;
// 使用后需要先释放data再释放String
10. 位域(Bit Fields)
允许按位分配结构体成员:
struct {
unsigned int is_keyword : 1; // 1位
unsigned int is_extern : 1;
unsigned int is_static : 1;
unsigned int : 5; // 5位未使用
unsigned int type : 4; // 4位
} flags;
11. 柔性数组(Flexible Array Member)
C99支持的动态大小结构体:
struct flex_array {
int length;
double data[]; // 柔性数组成员必须最后
};
struct flex_array *fa = malloc(sizeof(struct flex_array) + 10*sizeof(double));
fa->length = 10;
12. 结构体高级特性
12.1 匿名结构体(C11)
struct {
int x;
int y;
} point = {1, 2};
12.2 结构体复合字面量
printStudent((struct Student){"临时学生", 20, 85.5});
12.3 结构体比较
不能直接使用==比较,需要逐个成员比较:
int compareStudents(struct Student a, struct Student b) {
return strcmp(a.name, b.name) == 0 &&
a.age == b.age &&
a.score == b.score;
}
13. 结构体常见应用场景
-
表示复杂数据结构(如链表、树节点)
-
封装相关数据(如坐标点、学生记录)
-
函数返回多个值
-
硬件寄存器映射
-
网络协议数据包格式
-
文件格式描述
14. 结构体使用注意事项
-
结构体赋值是值拷贝,对大型结构体可能效率低
-
结构体作为参数传递时,通常传递指针更高效
-
结构体包含指针时,需要特别注意内存管理
-
不同平台可能有不同的对齐方式,影响结构体大小
-
结构体不能包含自身类型的实例(但可以包含指针)
-
结构体成员默认是public的(C++中才有访问控制)
15. 结构体与联合体(Union)的区别
特性 | 结构体(struct) | 联合体(union) |
---|---|---|
成员存储 | 独立内存空间 | 共享内存空间 |
总大小 | 所有成员大小和(考虑对齐) | 最大成员大小 |
同时访问 | 可以 | 只能访问一个 |
用途 | 组合相关数据 | 节省内存/类型转换 |
16. 结构体与C++类的比较
-
C结构体只有数据成员(C++结构体可以有函数)
-
C结构体没有访问控制(默认public)
-
C结构体没有继承、多态等面向对象特性
-
C结构体更轻量级,适合底层编程