C语言程序设计—结构体 的整理笔记,若有错误,欢迎指正。
C语言程序设计—结构体(Part Ⅱ)
C语言允许用户自己建立由不同类型数据组成的组合型的数据结构,它称为结构体(structure)。
自己建立结构体类型
- 声明一个结构体类型的一般形式为:
struct 结构体名
{成员表列};
- 成员表列中的各个成员都应进行类型声明,即:
类型名 成员名;
例1:
struct student //student为结构体名
{
int num; // 学号为整型
char name[20]; //姓名为字符串
float score; //成绩为实型
}; //注意最后有一个分号
程序分析:
- 上面由程序设计者指定了一个新的结构体类型struct student(struct是声明结构体类型时所必须使用的关键字,不能省略),它向编译系统声明这是一个“结构体类型”,它包括num、name、score三个不同类型的子项。
- 经过上面的指定,struct student就是一个在程序中可以使用的合法类型名,它和系统提供的标准类型(如int、char、float、double等)具有类似的作用,都可以用来定义变量的类型,只不过int等基本类型是系统已定义了的,而结构体类型是根据用户需要由程序设计者在程序中指定的。
- !注意:结构体类型的名字是由一个关键字struct和结构名二者组合而成的(例如struct student)。结构体名是由用户指定的,又称“结构体标记”(structure tag)以区别于其他的结构体类型。上面的结构体声明中student就是结构体名(结构体标记)。
- 大括号内是该结构体所包括的子项,称为结构体的成员(member)。上例中的num、name、score等都是成员。对各成员都应进行类型声明,“成员表列”(member list)也称为“域表"(field list),每一个成员是结构体中的一个域。成员名命名规则与变量名相同。
说明:
- 结构体类型并不是只有一种,而是可以设计出许多种结构体类型,例如除了可以建立上面的struct student结构体类型外,还可以根据需要建立名为struct teacher、struct worker、struct date等结构体类型,各自包含不同的成员。
- 成员可以是另一个结构体变量(就是在一个结构体内包含了另一个结构体作为其成员)。
例2:
struct date //声明一个结构体类型struct date
{
int month; //月
int day; //日
int year; //年
};
struct student //声明一个结构体类型struct student
{
char sex; //性别为字符型
int age; //年龄为整型
struct date birthday; //生日是struct date类型
};
程序分析:
- 先声明一个struct date类型,它代表“日期”,包括3个成员: month(月)、day(日)、year(年)。然后在声明struct student类型时,将成员birthday指定为struct date类型。已声明的类型struct date与其他类型(如int、char)一样可用来定义成员的类型。
定义结构体类型变量
- 以下三种方法都可以定义结构体变量
- 先声明结构体类型,再定义该类型的变量。
例3:
struct student //student为结构体类型名
{
int num; // 学号为整型
char name[20]; //姓名为字符串
float score; //成绩为实型
};
struct student student1,student2; //student1、student2为结构体变量名
在定义了结构体变量后,系统会为之分配内存单元。根据结构体类型中包含的成员情况,在Visual Studio 2019中,student1和student2在内存中占28个字节。(int:4 + char:1×20 + float:4)
这种方式是声明类型和定义变量分离,在声明类型后可以随时定义变量,比较灵活。
- 在声明类型的同时定义变量
其一般形式为:
struct 结构体名
{
成员表列
}变量名表列;
例4:
struct student //声明一个结构体类型struct student
{
int num; // 学号为整型
char name[20]; //姓名为字符串
float score; //成绩为实型
}student1,student2; //student1、student2为结构体变量名
- 声明类型和定义变量放在一起进行,能直接看到结构体的结构,比较直观,在写小程序时用此方式比较方便,但写大程序时,往往要求对类型的声明和变量的定义分别在不同的地方,以使程序结构清晰,便于维护,所以不宜用这种方式。
- 声明结构体类型仅仅是声明了一个类型,系统并不为之分配内存,就如同系统不会为类型int分配内存一样。只有当使用这个类型定义了变量时,系统才会为变量分配内存。所以在声明结构体类型的时候,不可以对里面的变量进行初始化。
- 不指定类型名而直接定义结构体类型变量
其一般形式为:
struct
{
成员表列
}变量名表列;
例5:
struct
{
int num; // 学号为整型
char name[20]; //姓名为字符串
float score; //成绩为实型
}student1,student2; //student1、student2为结构体变量名
- 指定了一个无名的结构体类型,它没有名字(不出现结构体名)。显然不能再以此结构体类型去定义其他变量,这种方式用得不多。
说明:
- 结构体类型与结构体变量是不同的概念。只能对变量赋值、存取或运算,而不能对类型赋值、存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间。
- 结构体类型中的成员名可以与程序中的变量名相同,但二者不代表同一对象。例如,程序中可以另外定义一个变量num,它与struct student中的num是两回事,互不于扰。
- 对结构体变量中的成员(即“域”),可以单独使用,它的作用与地位相当于普通变量。
结构体变量的初始化和引用
- 在定义结构体变量时,可以对它初始化,然后可以引用这个变量。
例:把一个学生的信息放在一个结构体变量中,然后输出这个学生的信息。
#include<stdio.h>
int main()
{
struct student //声明结构体类型 struct student
{
int num;
char name[20];
char sex;
char addr[20];
}student1= {10101, "Li Lin", 'M',"123 Beijing Road"}; //对变量student1初始化
printf("NO.:%d\nname:%s\nsex:%c\naddress:%s\n", student1.num, student1.name, student1.sex, student1.addr);
return 0;
}
//输出结果为:
NO.:10101
name:Li Lin
sex:M
address:123 Beijing Road
程序分析:
- 程序中指定了一个结构体名为student的结构体类型,有4个成员(num、name、sex、addr)。
- 在声明类型的同时定义了结构体变量student1,这个变量具有struct student类型所规定的结构。
引用结构体变量应遵守以下规则:
-
可以引用结构体变量中成员的值,引用方式为:
结构体变量名,成员名
例如,student1.num表示student1变量中的num成员,即student1的num(学号)项,在程序中可以对变量的成员赋值。
例:student1.num=10010;
“.”是成员运算符,它在所有的运算符中优先级最高。
!注意:不能通过结构体变量名来得到结构体变量中所有成员的值,只能对结构体变量中的各个成员分别进行输入和输出。
例:(错误❌)printf("%s\ n", student1); //企图用结构体变量名输出所有成员的值
-
如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。
#include<stdio.h>
int main()
{
struct date //声明一个结构体类型struct date
{
int month; //月
int day; //日
int year; //年
};
struct student //声明一个结构体类型struct student
{
char sex; //性别为字符型
int age; //年龄为整型
struct date birthday; //生日是struct date类型
}student1 = { 'M' , 21, {10,31,1999} }; //对变量student1初始化
printf("sex:%c\nage:%d\nmonth:%d/n", student1.num, student1.birthday.month);
return 0;
}
//输出结果为:
sex:M
age:21
month:10
程序分析:
- 如果在结构体中包含另一个结构体struct date类型的变量birthday,则引用成员的方式为:
student1.birthday.month;
- 不能用student1.birthday来访问student1变量中的成员birthday,因为birthday本
身是一个结构体变量。
- 对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
如例4中:
student2.score=studentl.score; //(赋值运算)
sum=student1.score+student2.score; //(加法运算)
student1.age++; //(自加运算)
- 由于“."运算符的优先级最高,因此 student.age++是对student.age进行自加运算,而不是先对age进行自加运算。
- 同类的结构体变量可以互相赋值,例:
student1=student2; //假设student1和student2已定义为同类型的结构体变量
例5:
#include<stdio.h>
int main()
{
struct date //声明一个结构体类型struct date
{
int month; //月
int day; //日
int year; //年
};
struct student //声明一个结构体类型struct student
{
int num; // 学号为整型
char name[20]; //姓名为字符串
float score; //成绩为浮点型
struct date birthday; //生日是struct date类型
}student1 = { 10101, "Li Lin", 71.6, {10,31,1999} }, student2 = { 20202, "Xi Xin",82.4, {12,25,1998} };
printf("NO.:%d\nname:%s\nscore:%.2f\nmonth:%d\n", student1.num, student1.name, student1.score, student1.birthday.month);
printf("\n");
student1 = student2;
printf("NO.:%d\nname:%s\nscore:%.2f\nmonth:%d\n", student1.num, student1.name, student1.score, student1.birthday.month);
return 0;
}
//输出结果为:
NO.:10101
name:Li Lin
score:71.60
month:10
NO.:20202
name:Xi Xin
score:82.40
month:12
程序分析:
- 由于student2是一个“组合项”,内放有关联的一组数据,因此,同类的结构体变量进行赋值时,全部成员的值会随之改变。例5中将结构体变量student2赋值给student1,从输出结果来看,赋值后,student1中所有成员的值都发生了变化。
例6:输出两个学生的学号、姓名和成绩,输出成绩较高的学生的学号、姓名和成绩。
#include<stdio.h>
int main()
{
struct student
{
int num;
char name[20];
float score;
}student1, student2;
scanf("%d%s%f", &student1.num, student1.name, &student1.score);
scanf("%d%s%f", &student2.num, student2.name, &student2.score);
printf("The higher score is:\n");
if (student1.score > student2.score)
printf("%d %s %.2f\n", student1.num, student1.name, student1.score);
else if (student1.score < student2.score)
printf("%d %s %.2f\n", student2.num, student2.name, student2.score);
else
{
printf("%d %s %.2f\n", student1.num, student1.name, student1.score);
printf("%d %s %.2f\n", student2.num, student2.name, student2.score);
}
return 0;
}
//输入:
10101 Li 71.6
20202 Xi 82.4
//输出结果为:
The higher score is:
20202 Xi 82.40
程序分析:
- 用scanf()函数输入结构体变量时,必须分别输入它们的成员的值,不能在scanf函数中使用结构体变量名一次性输入全部成员的值。
- !注意:在scanf函数中,成员student.num和student.score的前面都有地址符&,而在student1.name前面没有&。这是因为name是数组名,本身就代表地址,故不能画蛇添足地再加一个&。
- 从这里可以看到利用结构体变量的好处:由于student是ー个“组合项”,内放有关联的一组数据,student.score是属于student变量的一部分,因此如果确定了student1.core是成绩较高的,从而输出student的全部信息是轻而易举的。因为它们本来是互相关联,绑定在一起的,如果用普通变量是难以方便地实现这一目的的。