结构体
引子
假设我们想统计学生的成绩,每一个学生的统计中都要包含以下内容:学生姓名、学号、成绩。我们已经知道,可以使用字符数组(char
)表示姓名,整型(int
)表示年龄,长整型(long int
)表示学号,浮点型(float
)表示成绩。这里每一位学生的数据都是一个整体。正因如此,我们可以使用结构体来表示它们。
- A:为什么要使用结构体?
- B:使用结构体的好处就是将不同的数据类型整合在一起,方便程序员在后续进行修改和增添。
结构体基础
结构体声明和初始化
在C语言中,结构体通过关键字 struct
声明 ,struct的全称为structured data type(结构化数据类型)。
结构体的声明
在程序中,我们声明结构体的位置一般在函数外。如果把结构体的声明放在某一个函数内,比如主函数内,那么结构体就只能在主函数内应用。
struct Student
{
char name[50];
int age;
long int StudentID;
float score;
}student1;
上面的代码中,struct Student
是我们声明的数据类型,大括号内的代码块声明数据类型。student1
是对应于stuct Studnt
的变量名,每个结构化数据类型都可以对应多个不同的变量。
使用typedef为结构命名
在创建结构化数据类型时,我们每一次都不得不使用struct
关键字。为了简化这种表述,可以在struct
之前使用typedef
为结构起一个别名。
typedef struct STUDENT//这里的typedef表示为结构起一个新的别名
{
char name[50];
int age;
long int StudentID;
float score;
}student;//stedent将成为struct STUDENT的别名
结构体初始化
在声明结构体之后,我们可以在函数中直接初始化结构体:
"."运算符
在C语言中,我们可以使用“点运算符”(.
)来访问结构体中的元素。
访问结构体中的元素
我们可以通过点操作符(.
)访问结构体zhangsan
中 的某一个元素:
#include <stdio.h>
struct Student {
char name[50];
int age;
long int StudentID;
float score;
};
int main()
{
struct Student zhangsan = { "zhangsan",18,20230001,100};
printf("%f\n",zhangsan.score);
return 0;
}
更改结构体中的元素
如果我们想单独更改结构体中的某一个信息,我们不必要再次初始化整个结构体,可以直接针对某个元素更改数据。
#include <stdio.h>
struct Student {
char name[50];
int age;
long int StudentID;
float score;
};
int main()
{
struct Student zhangsan = { "zhangsan",18,20230001,100};
printf("%f\n",zhangsan.score);
zhangsan.score=61;
printf("%f\n", zhangsan.score);
struct Student lisi = { "lisi",18,20230002,100 };
return 0;
}
下面是程序运行结果:
结构体数组
- A:假如一个班级有四十个人,我们难道只能像上面一样,逐一初始化结构体吗?
- B:那也太麻烦了,显然是不行的。
- A:那怎么办?
- B:我们可以使用结构体数组简化步骤。
下面是一个例子:
struct Student {
char name[50];
int age;
long int StudentID;
float score;
}class1[41];
直接初始化
结构体数组可以使用下面的代码在程序中直接进行初始化:
struct Student class2[2] = {
{"Alice", 30, 5.5},
{"Bob", 25, 5.8}
};
从外部导入数据
如果想从键盘或文件中导入数据,我们可以使用循环来声明数组(下面的代码使用的是标准输入):
#include <stdio.h>
struct Student {
char name[50];
int age;
long int StudentID;
float score;
}student[41];
int main()
{
for (int i = 0; i <= 40; i++)
{
scanf("%s%d%ld%f", student[i].name, student[i].age, student[i].StudentID, student[i].score);
}
return 0;
}
嵌套结构体
结构体可以嵌套使用,即一个结构体成为了另一个结构体的元素。这种嵌套的结构体方便了我们模拟复杂的数据结构。
struct Date {
int day;
int month;
int year;
};
struct Student {
char name[50];
int age;
long int StudentID;
float score;
struct Date birthday;//这里是嵌套的结构体
}
结构体的深入理解
存储器中的结构体
- 当我们声明新结构化变量时,实际上是告诉计算机一个模板,告诉计算机这个变量长什么样。
- 当我们初始化新的结构体变量时,计算机会在存储器中按照模板创建一片足够大的空间。当然,同一个结构体的变量在存储器中的地址是相邻的。
- 当我们把一个结构变量赋值给另一个结构变量时,计算机会在存储器中创建另一片空间,大小和原来完全相同,然后把每一个字段复制过去。
从第3点可知,如果我们想要在某一个函数中调用结构体,结构体在实参与形参的传输过程中,还是会出现那个老问题——函数中参数地址和原地址不同。
为了解决这个问题,我们必须调用结构指针。
结构指针
结构体和数组的区别
结构体与数组不一样,它的地址虽然相邻,但是却不能通过编号、加1和减1等方式进行访问。
- A:那如果想用指针访问结构体那应该怎么办?
- B:我们可以使用
.
操作符访问结构体中的元素,还可以使用->
符号访问结构体。
结构指针的应用
struct Student *ptr = &zhangsan;
printf("%s is %d years old.\n", ptr->name, ptr->age);
printf("%s is %d years old.\n", (*ptr).name, (*ptr).age);
- A:为什么要使用
(*ptr)->name
而不是*ptr->name
- B:这里的
*ptr->name
与*(ptr->name)
相同,如果这里的ptr
是指针变量,那么从第一步开始就无法完成。
总结:
这篇文章中主要讲了结构体的有关知识点,可以说是比较详细的,可是在结构体应用方面是比较粗浅的,结构体还有更多的深奥的秘密等待着我们的学习与探寻。