注意:①本篇依靠代码进行学习,实现代码理论交互
②(作者简化了原视频的内容)原文可看B站教学视频C语言结构体详解【干货】_哔哩哔哩_bilibili
一.代码总览
#include<stdio.h>
typedef struct Birthday
{
int year;
int month;
int day;
}Birthday;
typedef struct Student
{
int id;
char *name;
int age;
float score;
Birthday birthday;
}Student;
void printStudentInfo(Student *pStu)
{
printf("学号:%d\t姓名:%s\t年龄:%d\t成绩:%.2f\t生日:%d-%d-%d\n",
pStu->id, pStu->name, pStu->age, pStu->score, pStu->birthday.year, pStu->birthday.month, pStu->birthday.day);
}
int main()
{
Student stu1 = {1001, "张三", 27, 100, {2021, 1, 24}};
Student stu2 = {1002, "李四", 25, 99, {2000, 2, 10}};
Student *pStu = &stu1;
printStudentInfo(pStu);
pStu = &stu2;
printStudentInfo(pStu);
return 0;
}
二.代码讲解
1. 结构体的定义
typedef struct Birthday
{
int year;
int month;
int day;
}Birthday;
这里我们定义了一个名为 `Birthday` 的结构体,包含了 `year`(年)、`month`(月)和 `day`(日)三个整数成员。使用 `typedef` 简化了定义,允许我们直接用 `Birthday` 来声明变量,而不必每次都写 `struct Birthday`
类似地:
typedef struct Student
{
int id;
char *name;
int age;
float score;
Birthday birthday;
}Student;
结构体 `Student` 包含五个成员:`id`(学号)、`name`(名字,指针类型)、`age`(年龄)、`score`(成绩)以及一个嵌套的 `Birthday` 结构体。通过嵌套结构体,我们可以将复杂的数据结构分解为简单的部分,便于管理和操作。
2. 结构体的初始化
在 `main` 函数中,我们声明并初始化了两个 `Student` 结构体对象 `stu1` 和 `stu2`:
Student stu1 = {1001, "张三", 27, 100, {2021, 1, 24}};
Student stu2 = {1002, "李四", 25, 99, {2000, 2, 10}};
初始化时,结构体的成员按定义的顺序赋值。特别注意 `birthday` 的赋值方式,必须用 `{}` 来表示子结构体的成员初始化。这里 `name` 是一个字符串指针,指向常量字符串 `"张三"` 和 `"李四"`,因此可以直接赋值。
3. 结构体指针的使用
结构体可以通过指针进行传递和操作。在这个例子中,函数 `printStudentInfo` 使用了一个指向 `Student` 的指针 `pStu`。
void printStudentInfo(Student *pStu)
{
printf("学号:%d\t姓名:%s\t年龄:%d\t成绩:%.2f\t生日:%d-%d-%d\n",
pStu->id, pStu->name, pStu->age, pStu->score, pStu->birthday.year, pStu->birthday.month, pStu->birthday.day);
}
通过 `->` 操作符,我们可以访问指针指向的结构体成员。`pStu->id` 意味着访问指针 `pStu` 指向的 `Student` 对象的 `id` 成员。如果直接操作结构体(非指针),则使用 `.` 操作符(如 `stu1.id`)。
4. 函数传递结构体指针
在 `main` 函数中,我们将 `stu1` 和 `stu2` 的指针传递给 `printStudentInfo` 函数:
Student *pStu = &stu1;
printStudentInfo(pStu);
pStu = &stu2;
printStudentInfo(pStu);
`pStu` 指向 `stu1` 的地址(`&stu1`),然后再指向 `stu2`,每次调用时都会打印对应学生的信息。传递结构体指针比传递整个结构体更加高效,尤其是当结构体较大时,指针传递只会复制地址,而不是整个结构体。
5. 结构体与内存布局
结构体的成员在内存中是连续存储的,但不同数据类型的对齐方式可能会造成“内存填充”,这会导致结构体的实际大小大于各成员大小之和。可以通过 `sizeof()` 函数查看结构体的大小,例如 `sizeof(Student)`,这在性能优化中比较重要。
6. 其他结构体知识点补充
1) 结构体数组
可以定义结构体数组,用于存储多个同类对象。例如:
Student students[3] = {
{1001, "张三", 27, 100, {2021, 1, 24}},
{1002, "李四", 25, 99, {2000, 2, 10}},
{1003, "王五", 23, 98, {1999, 5, 12}}
};
这允许批量处理一组结构体对象,如循环遍历所有学生并打印信息。
2) 结构体指针数组
当需要指针数组指向多个结构体对象时:
Student *pStudents[3] = {&stu1, &stu2, &stu3};
然后可以通过指针数组访问和操作每个结构体对象。
3) 结构体作为函数参数
可以将整个结构体作为函数参数传递,但为了节省内存,通常传递指针:
void updateScore(Student *stu, float newScore) {
stu->score = newScore;
}
通过指针可以直接修改结构体的成员值。
7. 结构体应用场景
结构体在许多实际应用中非常重要,例如:记录信息:学生信息、职工信息、产品数据等。复杂数据管理:例如定义二维坐标 `struct Point { int x; int y; };,或者定义网络协议数据包等。
三.总结
这段代码展示了如何定义和使用结构体,并通过结构体指针来操作和传递数据。重要的知识点包括:结构体的定义与嵌套、结构体的初始化、结构体指针与指针操作、结构体的内存布局和高效使用结构体,是C语言中管理复杂数据的基础,对于构建更复杂的数据结构和应用程序至关重要。