1.问题引入
假设你现在是一位成绩管理员,有以下数据需要你处理:
每位学生信息有:学号、姓名、性别、年龄、住址、成绩等。
那么,该如何用C语言相关数据类型、语句描述这些不同类型的数据呢?
此时你可能考虑到定义不同的数据分别对应存储,但是这样比较分散,每一个学生都有这些属性,那么要定义很多对应数据类型的变量来存储相应数据。
比如:
5个学生的姓名,那么,则要定义5个string(字符串)类型变量,分别是s1、s2、s3、s4、s5存储5个学生的信息。看起来没什么问题,那么如果是50个学生呢?难道要定义50个string(字符串)类型变量存储数据?
很显然,这是不妥当的,那么该如何更好的“集中、高效、方便、统一”的来存储这些数据呢?那就要使用到我们今天的主角——“结构体”。
2.什么是“结构体”?
“结构体” 即为 一种构造类型数据。由若干不同类型的数据项组成,构成结构体的各个数据项称为结构体成员。
比如我们上面说到的每位学生的信息——学号、姓名、性别、年龄、住址、成绩,则可分别定义为如下类型的数据:
char number[8]; //学号是字符数组类型
char name[30]; //姓名是字符数组类型
char sex; //性别是字符型
int age; //年龄是整型
char address[60]; //住址是字符数组类型
float grade; //成绩是整型数组类型
其中,每一个数据项均为结构体成员。
3.结构体“作用”是什么?
我们用“结构体”来存储若干“数据项”在一个整体中,结构体的作用 则为:“使结构体类型的变量共同拥有结构体内部定义的若干数据项”,使每一个变量对应一组数据,但不用多次定义数据类型。
举个例子:
仍用上面我们说到的学生信息,我们定义如下结构体(这里如果你不太清楚结构体类型的定义,先不要担心,我们先来了解一下“结构体”作用,会对我们理解更有帮助):
struct Student {
char number[8]; //学号是字符数组类型
char name[30]; //姓名是字符数组类型
char sex; //性别是字符型
int age; //年龄是整型
char address[60]; //住址是字符数组类型
float grade; //成绩是整型数组类型
};
我们定义了一个结构体,结构体名字为Student,包含学号、姓名、性别、年龄、住址、成绩六个数据项,现在我们在定义该结构体类型变量:
struct Student s1,s2,s3,s4,s5;//定义结构体类型变量
如上,定义了5个结构体类型变量,那么这5个变量就均具备该结构体的每一个数据成员,即每个变量都拥有学号、姓名、性别、年龄、住址、成绩六个数据项。我们可以看出,采用“结构体”构造数据相比较前面我们分别定义变量来表示数据明显减少了数据的冗余,提高了执行效率。
4.结构体的 “3种” 定义方式
对“结构体”有了初步了解后,接下来我们就来看看结构体的 “3种” 定义方式,主要区别在于“形式上不同”,本质相同。
为便于大家理解,熟悉+代码结合方式为大家解析。
(1)第1种定义方式(一般形式)
也称:“间接定义 ”,定义结构体的格式如下:
struct 结构体名 {
若干数据项;
} ;
其中,struct为关键字; 结构体名是用户定义的类型标识。 { }中是组成该结构体的成员。成员的数据类型可以是C语言所允许的任何数据类型。
采用这种方式定义的结构体,创建变量的格式为:
struct 结构体名 结构体变量名 ;
!!!注意:上述定义结构体以及创建变量 末尾的分号 “ ;” 一定要有,不能缺少。
接下来看代码实测以及运行结果:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//结构体定义形式 1
struct Student{
char number[8];//学号是字符数组类型
char name[30];//姓名是字符数组类型
char sex;//性别是字符型
int age;//年龄是整型
char address[60];//住址是字符数组类型
float grade;//成绩是整型数组类型
};
//主函数
int main(){
//struct 结构体名 结构体变量名;
//1.创建 结构体变量
struct Student s1;//定义结构体变量 s1
//初始化信息
printf("请输入学生信息:学号、姓名、性别、年龄、住址、成绩 \n\n");
scanf("%s %s %c %d %s %f",s1.number,s1.name,&s1.sex,&s1.age,s1.address,&s1.grade);
//输出信息
printf("\n\n学号:%s 姓名:%s 性别:%c 年龄:%d 住址:%s 成绩:%f",s1.number,s1.name,s1.sex,s1.age,s1.address,s1.grade);
return 0;
}
运行结果:
(2)第2种定义方式
格式如下:
struct 结构体名 {
若干数据项;
} 结构体变量名 ;
其中,struct为关键字; 结构体名是用户定义的类型标识。 { }中是组成该结构体的成员。成员的数据类型可以是C语言所允许的任何数据类型,结构体变量名 为该结构体定义变量名称(用户自定义)。
!!!注意:上述定义结构体 末尾的分号 “ ;” 一定要有,不能缺少。
不难看出,相比于第1种形式,只是将“结构体变量”附加在大括号后面进行定义,省略了“struct 结构体名 结构体变量名 ;”这条语句,其余本质相同。
接下来看代码实测以及运行结果:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//结构体定义形式 2
//形式2与形式1的区别在于:
//在形式1基础上去掉了"struct 结构体名 结构体变量名;"这一语句
//直接在 大括号 后面定义结构体变量
struct Student{
char number[8];//学号是字符数组类型
char name[30];//姓名是字符数组类型
char sex;//性别是字符型
int age;//年龄是整型
char address[60];//住址是字符数组类型
float grade;//成绩是整型数组类型
}s1;// 定义结构体变量 s1
//主函数
int main(){
//初始化信息
printf("请输入学生信息:学号、姓名、性别、年龄、住址、成绩 \n\n");
scanf("%s %s %c %d %s %f",s1.number,s1.name,&s1.sex,&s1.age,s1.address,&s1.grade);
//输出信息
printf("\n\n学号:%s 姓名:%s 性别:%c 年龄:%d 住址:%s 成绩:%f",s1.number,s1.name,s1.sex,s1.age,s1.address,s1.grade);
return 0;
}
运行结果:
(3)第3种定义方式
格式如下:
struct {
若干数据项;
} 结构体变量名 ;
其中,struct为关键字; { }中是组成该结构体的成员。成员的数据类型可以是C语言所允许的任何数据类型,结构体变量名 为该结构体定义变量名称(用户自定义)。
!!!注意:上述定义结构体 末尾的分号 “ ;” 一定要有,不能缺少。
不难看出,相比于第2种形式,只是去掉了“结构体名”,其余本质相同。
接下来看代码实测以及运行结果:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//结构体定义形式 3
//形式3与形式2的区别在于: 在形式2的基础上,去掉了“结构体名”
struct {
char number[8];//学号是字符数组类型
char name[30];//姓名是字符数组类型
char sex;//性别是字符型
int age;//年龄是整型
char address[60];//住址是字符数组类型
float grade;//成绩是整型数组类型
}s1;// 定义结构体变量 s1
//主函数
int main(){
//初始化信息
printf("请输入学生信息:学号、姓名、性别、年龄、住址、成绩 \n\n");
scanf("%s %s %c %d %s %f",s1.number,s1.name,&s1.sex,&s1.age,s1.address,&s1.grade);
//输出信息
printf("\n\n学号:%s 姓名:%s 性别:%c 年龄:%d 住址:%s 成绩:%f",s1.number,s1.name,s1.sex,s1.age,s1.address,s1.grade);
return 0;
}
运行结果:
(4)总结
不难看出,形式1、形式2、形式3的主要区别体现在“有无结构体名”、“变量的定义位置”不同。
第1种形式为“一般形式”,应熟悉掌握。
形式2相比于形式1——只是 将“结构体变量”附加在大括号后面进行定义,省略了“struct 结构体名 结构体变量名 ;”这条语句,其余本质相同。
形式3相比于形式2——只是 去掉了“结构体名” ,其余本质相同。
因此,掌握了“形式1”,稍作变动即可得到形式2和形式3。
5.内容补充
(1)结构体变量也可以是其他C语言中的数据类型,如数组、指针等。
如下代码所示:
//结构体
struct date{
int year;
int month;
int day;
} DATE[10],*strDate;
(2)结构体变量在定义时也可以进行初始化 、 赋值 以及内嵌其他结构体,此时系统将按数据成员对应赋值。
如下代码所示:
//结构体1
struct date{
int year;
int month;
int day;
};
//结构体2
struct student{
char num[8];
char name[20];
char sex;
struct date birthday;//内嵌结构体1
float score;
} a={"9606011","Li ming",'M',{1977,12,9},83},b,c;//定义变量
c = a;//赋值
(3)对结构体变量a进行 &a 运算,可以得到a的首地址,它是结构体类型指针。
如下代码所示:
//结构体1
struct date{
int year;
int month;
int day;
};
//结构体2
struct student{
char num[8];
char name[20];
char sex;
struct date birthday;//内嵌结构体1
float score;
} a; //若对 a 取地址“&a”,可以得到a的首地址,为结构体类型指针
(4)如果初值个数少于结构体成员个数, 则将无初值对应的成员赋以0值。如果初值个数多于结构体成员个数, 则编译出错。
如下代码所示:
//结构体1
struct date{
int year;
int month;
int day;
};
//结构体2
struct student{
char num[8];
char name[20];
char sex;
struct date birthday;//内嵌结构体1
float score;
} a={"9606011","Li ming",'M',{1977,12,9},83},
b={"9608025","Zhang liming",'F',{1978,5,10},87};
//a 和 b都没有问题,数据相对应,不多不少
/* 1.如下缺少赋值:
c={"9606011","Li ming",'M',{1977,12,9}};
//(1)变量 c 在末尾少了“score”的数据值,因此缺少1个值,系统会设置score=0 。
//(2)因此:元素的个数有时可以省略,根据赋初值时结构体常量的个数确定数组元素的个数。
*/
/* 2.如下出错赋值——数据不对应:
d={"9606011","Li ming",'M',{1977,12,9},83,1111111111};
//(1)变量 d 在末尾多了一个数据值“1111111111”,是多于的,系统会报错。
//(2)因此:数据必须一一对应,不可超出个数,同时类型也必须一致。
// 如 int 类型不能为赋值为 float 类型。
*/