AutoLeaders控制组——(C语言)结构体* 10.21日笔记-------------------------------邱文硕
序言————————————————笔记来源于《C语言程序设计·在线实践·微课视频》、凯翁C语言教程及自我总结,如有不足,请见谅。
说明:ALN表示auto leaders note即“要点” ALQ表示auto leaders question即“问题”
1,结构体
-
结构体的引入
为什么要引入结构体,举个栗子,如果一个学生的信息包括学号,姓名,成绩,性别,年龄等等,请设计数据类型来描述学生信息。
由之前学过的内容可知,没有那个数组能够把这样一组各类的信息储存其中,没有任何一个数据类型可以描述这样一组信息。运用所学的知识,我们只能写出以下形式:
int id,age; char name[20]; float grades; char sex; // m/f ```
由上可知,这几个变量实际上是相互独立的,难以反映他们之间的内在联系。而且很多类型是不相同的,无法在一个数组中表示。
此时就需要一个东西能把这些不同类型的数据装在一起,而这个东西就是所谓的结构体。
-
结构体的含义
结构体有两层含义,一层含义是“结构体类型”,由用户根据需要自建一种新的数据类型,即声明一个结构体类型,它告诉编译器如何表示数据,但此时并未为数据分配空间;另一层含义是“结构体变量”,通过用户自建的结构体类型去定义对应的结构体变量,系统为结构体变量分配具体的储存空间。
-
结构体类型的声明
语法 示例 说明 struct结构体名{类型名1 成员;类型名2 成员;类型名3 成员;···} struct Student{ int id;char name[20];float grades;}; (1)struct为声明结构体的关键字,不能省略。(2)结构体名应符合C语言自定义标识符规则;结构体名可以省略,省略的结构体称为无名结构体。(3)结构体的每一个成员可以是一个基本数据类型,也可以属于另一个结构体类型。(4)结构体所包含的所有成员都包含在一对大括号之间。(5)末尾的大括号后不要忘记加上分号。 ALN:结构体类型与C语言基本数据类型作用相似,都可以用来定义变量。基本数据类型是由系统构造的,而结构体数据类型是由用户自己构造的。在一个程序中可以定义多个不同的数据类型。
结构体的成员可以属于另一个结构体类型。如:
struct Date { int year,month,day; }; struct Student //表示学生的结构体struct Student { int id; char name[20]; struct Date birthday; //成员birthday属于struct Date类型 float s; };
先声明一个表示日期的结构体struct Date类型,它包括3个成员:year、month、day。然后在声明struct Student类型时,将成员birthday指定为struct Date类型。
结构体类型是一个数据类型,对它的声明仅仅是指明了该类型的名称和数据结构,是对数据类型的一种抽象说明,此时并没有定义,没有具体的数据,自然不会分配储存单元。为了能在程序中使用这些自建的结构体类型,需要用结构体类型来定义变量,只有在结构体变量中才能储存真正的数据。
再回到上面的代码,定义了一个Date birthday之后,系统为其分配储存空间时,结构体变量分配到储存单元数量取决于结构体中所包含的成员数量以及每个成员所属的数据类型,即结构体变量在内存中所占字节至少(有时一个结构体变量定义完之后,为了满足存储对齐的需要,所占的实际内存空间可能大于结构体所定义的空间)是它各个成员所占字节数之和。如Date birthday所占的字节数为4B+4B+4B=12B。
ALN:结构体类型和结构体变量是两个不同的概念。结构体变量是实体,分配内存单元,可以对其赋值、存取和运算。而结构体类型是抽象的,既不分配内存单元,也不进行赋值、存取和运算。每个结构体变量必定属于某种结构类型。
-
为结构体类型取别名
在C语言中,用户可以借助typedef为已存在的结构体类型取别名,在之后定义相应变量时可直接使用别名。
取别名的两种方式:
(1) typedef struct Student{···}STUDENT;将struct Student取名为STUDENT
(2)struct Student{···}; typedef struct Student STUDENT;
也可以在声明结构体类型的同时定义变量:
struct Student{···}stu1,stu2;
在声明结构类型的同时定义变量,但不指定结构体类型名:
struct {···}stu1,stu2;
这种形式省略了结构体类型名,在声明结构体类型的同时定义结构体变量,显然这个结构体类型不能在其他地方使用。这种方式一般不建议使用。
-
结构体变量的使用和初始化
程序中使用结构体变量时,主要通过对各个变量的引用来实现。所有变量都属于同一个结构体类型时,各成员的名称是一样的,因此需要在成员名前面用结构体变量进行限定,才能区分不同变量的成员,其形式如下:
结构体变量名.成员名
同时要注意以下四点:(1).可以引用结构体变量中的成员。
如
scanf("%d",&stu1.id); stu1.id=191324; strcpy(stu1.num,"Peter"); stu1.s=93.5;
(2)结构体变量不能整体输入输出,只能通过分别访问其中的各个成员来实现。
scanf("%d%s%f",&stu1); //错误 printf("%d,%s,%.1f",stu1); //错误 printf("%s",stu1); //错误 scanf("%d%s%f",&stu1.id,&stu1.name,&stu1.s);
(3)同类型的两个结构体变量不能整体输入输出,但可以整体赋值。如stu1=stu2;
(4)如果成员本身又是一个结构体类型,则要通过逐级访问的形式,用多个成员符,一级一级地找到最低一级为止。且只能对最低一级赋值。如stu1.birthday.month
2,结构体数组
-
结构体数组的定义
typedef struct Student { int id; char name[20]; float s; }STUDENT; STUDAENT stu[3]; //定义结构体数组stu
其内存储存情况如
-
结构体数组的引用与初始化
引用:
结构体数组的每一个元素都是结构体变量,对结构体数组元素的引用与结构体变量的引用方式大体相当。如上例中对第一位同学的姓名的表示为:stu[0].name stu[0]表示结构体数组元素变量名,name是结构体包含的内容。
初始化:
在结构体数组初始化时,要将每一个数组元素用花括号括起来,各个成员或数组元素之间用逗号间隔。
#include <stdio.h> struct Student { int id; char name[20]; float grades; }; typedef struct Student STUDENT; int main() { STUDENT stu[3]={{001,"Anney",92.5},{002,"Linda",91},{003,"Jack",93}}; int i; float sum=0; for(i=0;i<3;i++) sum+=stu[i].grades; printf("average=.1f\n",sum/3); return 0; }
3,结构体指针
既然结构体变量占有一定储存单元,同样的,它也有对应的指针,称之为结构体指针变量,是指向结构体类型数据的指针变量。和数组指针类似,结构体指针指向的也是结构体的内存空间首地址。
-
指向结构体变量的指针
指向一个结构体变量的指针,其值是该结构体变量所占内存空间的起始地址。
定义结构体指针变量的一般形式如表
语法 struct结构体名 * 指针变量名 示例 struct Student * p; 说明 struct Student 是一个已经声明的结构体类型,p是一个该类型的指针变量 结构体指针变量也能访问结构体变量中的各个成员,此时需要借助符号"->"如下
实际应用中大多采用形式1,这种形式更加直观清晰
#include <stdio.h> struct Student { int id; char name[20]; float grades; }; typedef struct Student STUDENT; int main() { STUDENT stu1; STUDENT *p; p=&stu1; scanf("%d%s%f",&stu1.id,&stu1.name,&stu1.grades); printf("No.%d,name:%s,score:%.1f\n",stu1.id,stu1.name,stu1.grades); printf("No.%d,name:%s,score:%.1f\n",p->id,p->name,p->s); printf("No.%d,name:%s,score:%.1f\n",(*p).id,(*p).name,(*p).s); return 0; }
最终,输出结果相同。
总结:如果p指向一个结构体变量例如stu1时,以下三种方式等价。
(1)stu.1+成员名。
(2)p->+成员名,例如p->id。
(3)(* )p.成员名,例如(*p).id。
指向结构体数组的指针
-
指向结构体数组的指针
定义一个指针指向数组时,指向其首地址;定义一个指针指向结构体时,指向其首地址。不出意外,定义一个指针指向结构体数组时,指向的就是该结构体数组所占空间的起始地址。例如:
STUDENT stu[3]; STUDENT *p; p=stu;
p是指向结构体数组stu的指针变量,保存了数组的首地址,即p指向该结构体数组的0号元素,p+1指向1号元素···
输入三个学生的学号、姓名、成绩,显示这三个人的所有信息
#include <stdio.h> struct Student { int id; char name[20]; float grades; }; typedef struct Student STUDENT; int main() { STUDENT stu[3]; STUDENT *p; int i; p=stu; for(i=0;i<3;i++) scanf("%d%s%f",&(p+i)->id,&(p+1)->name,&(p+i)->grades); for(i=0;i<3;i++) printf("No.%d name:%s grades:%1f\n",(p+i)->id,(p+i)->name,(p+i)->grades); return 0; }
ALN:当指针变量指向结构体数组时,应当注意一个结构体类型的指针变量只能保存一个结构体变量的地址或一个结构体数组元素的地址,不能保存某一个结构体变量或结构体数组成员的地址。p=&stu[0].id 是错误的表示。
以下几种运算的含义是:
p->s++:先使用p所指向的数组元素中的成员s的值,然后再使s的值加一。
++p->s:先将p所指向的数组元素的值加一,然后再使用s的值。
(p++)->s:先使用p当前指向的数组元素中成员s的值,然后再将p加一,即是p指向下一个元素。
(++p)->s:先使p加一,即是p指向下一个数组元素,然后再使用p指向的数组元素的成员s的值。
指向运算符->优先级高于自增运算符,把握好这一点,上面的就可以更好解释了
ALN:在容易引起歧义的地方,建议尽量使用圆括号限定其运算顺序,如果允许的话,也可以将一个表达式按运算顺序分成多个表达式书写。
4,结构体与函数
结构体与函数的关系可以从两方面来看,一方面是函数的参数为结构体类型(结构体变量的成员、结构体变量、结构体指针);另一方面是函数值是结构体类型的值。
-
结构体变量的成员作实参
输入三个学生的信息,包含:学号、姓名、三门课的成绩,请根据每人的平均分输出对应的等级,80分及以上为A,60~79为B,60以下为C.
#include <stdio.h> struct Student { int id; char name[20]; float grades[3]; //平均分 }; typedef struct Student STUDENT; char Rank(float ave); //函数声明,形参为float型变量 int main() { STUDENT stu[3]; int i,j; float sum; for(i=0;i<3;i++) { sum=0; scanf("%d %s",&stu[i].id,stu[i].name);//输入学号、姓名 for(j=0;j<3;j++) { scanf("%f",&stu[j].grades[j]); //输入三门课的成绩 sum=sum+stu[j].grades[j]; //计算总分 } stu[i].average=sum/3; //计算平均分 } for(i=0;i<3;i++) printf("%s--%c\n",stu[i].name,Rank(stu[i],average));//实参为结构体变量中的成员即grades[i] return 0; } char Rank(float ave) { if(ave>=80) return 'A'; else if(ave<60) return 'C'; else return 'B'; }
-
结构体变量作实参
将输出过程设计成函数output(),并以结构体变量作实参进行调用。
#include <stdio.h> struct Student { int id; char name[20]; float grades[3]; //平均分 float average; }; typedef struct Student STUDENT; char Rank(float ave); //函数声明,形参为float型变量 void output(STUDENT stu); int main() { STUDENT stu[3]; int i,j; float sum; for(i=0;i<3;i++) { sum=0; scanf("%d %s",&stu[i].id,stu[i].name);//输入学号、姓名 for(j=0;j<3;j++) { scanf("%f",&stu[j].grades[j]); //输入三门课的成绩 sum=sum+stu[j].grades[j]; //计算总分 } stu[i].average=sum/3; //计算平均分 } for(i=0;i<3;i++) output(stu[i]); return 0; } void output(STUDENT stu) { int j; printf("id.%d,name:%s,",stu.id,stu.name); for(j=0;j<3;j++) printf("%.1f",stu.s[j]); printf("Average:%.1f\n",stu.average); }
-
结构体变量的指针作实参
输入三个学生的信息(学号,姓名,一门课的成绩),输出这三个学生的信息
#include <stdio.h> struct Student { int id; char name; float grades; }; typedef struct Student STUDENT; void input(STUDENT * p); void output(STUDENT * p); int main() { STUDENT stu[3]; input(stu); output(stu); return 0; } void input(STUDENT * p) { int i; for(i=0;i<3;i++,p++) scanf("%d%s%f",&p->id,&p->name;&p->grades); } void output(STUDENT * p) { int i; for(i=0;i<3;i++,p++) printf("id.%d,name;%s,grades:%.1f\n",p->id,p->name,p->grades); }