第八章 结构体和共用体
第一节 结构体变量
-
什么是结构体?
将多种数据类型结合构建在一起的数据类型称为
结构体类型
;需要多个数据类型来表示某一信息时,可以使用结构体。例如学生李四和赵六参加比赛需要在网络上填写信息包含:存在不同的数据类型
name:lisi, age:23, sex:M, student_number:20193055; name:zhaoliu, age:22, sex:M, student_number:20193091;
代码示例:
#include <stdio.h> struct test { char name[30]; int age; char sex; int student_number; }; int main() { struct test lisi , zhaoliu; printf("请输入学生的姓名 年龄 性别 学号:"); scanf("%s %d %c %d",lisi.name,&lisi.age,&lisi.sex,&lisi.student_number); printf("参加比赛的学生有%s\t年龄:%d\t性别:%c\t学号:%d\n",lisi.name,lisi.age,lisi.sex,lisi.student_number); printf("请输入学生的姓名 年龄 性别 学号:"); scanf("%s %d %c %d",zhaoliu.name,&zhaoliu.age,&zhaoliu.sex,&zhaoliu.student_number); printf("参加比赛的学生有%s\t年龄:%d\t性别:%c\t学号:%d\n",zhaoliu.name,zhaoliu.age,zhaoliu.sex,zhaoliu.student_number); return 0; }
-
创建结构体类型:
结构体类型的创建方法:
struct 结构体名{ 类型名 成员表列1; 类型名 成员表列2; ··· };
说明:
-
创建结构体类型是指创建的数据类型;
-
结构体名是指对应创建的数据类型名,例如之前学习的int型、float型,其中int和float是数据类型名;
-
成员表列可以包含多个同类型及不同类型的数据,每个成员以
;
分隔。结构体的花括号{}
之后有一个;
-
成员也可以是另一个结构体类型。
代码示例:
struct score { int shuxue; int yuwen; int zhengzhi; char tiyu; }; struct test { char name[30]; int age; char sex; int student_number; struct score gaokao; };
-
-
定义结构体变量
在没有定义结构体之前,系统不会分配存储空间。为了使用结构体变量,需要在使用之前进行定义。
-
先创建结构体类型,再定义。
在文件的开头创建好结构体类型,然后在函数中使用定义结构体变量。定义的方法为:
struct 结构体名 结构体变量名 //例如: struct test lisi;
代码示例:
#include <stdio.h> struct test { char name[30]; int age; char sex; int student_number; }; int main() { struct test lisi , zhaoliu; printf("请输入学生的姓名 年龄 性别 学号:"); scanf("%s %d %c %d",lisi.name,&lisi.age,&lisi.sex,&lisi.student_number); printf("参加比赛的学生有%s\t年龄:%d\t性别:%c\t学号:%d\n",lisi.name,lisi.age,lisi.sex,lisi.student_number); printf("请输入学生的姓名 年龄 性别 学号:"); scanf("%s %d %c %d",zhaoliu.name,&zhaoliu.age,&zhaoliu.sex,&zhaoliu.student_number); printf("参加比赛的学生有%s\t年龄:%d\t性别:%c\t学号:%d\n",zhaoliu.name,zhaoliu.age,zhaoliu.sex,zhaoliu.student_number); return 0; }
-
在创建结构体类型的同时进行定义。
说明:结构体成员表名在结构体内唯一,但可以和其他变量名相同。
struct 结构体名 { 类型名 成员表列1 类型名 成员表列2 ··· }变量名表列;
代码示例:
#include <stdio.h> int main() { struct test { char name[30]; int age; char sex; int student_number; }lisi,zhaoliu; printf("请输入学生的姓名 年龄 性别 学号:"); scanf("%s %d %c %d",lisi.name,&lisi.age,&lisi.sex,&lisi.student_number); printf("参加比赛的学生有%s\t年龄:%d\t性别:%c\t学号:%d\n",lisi.name,lisi.age,lisi.sex,lisi.student_number); printf("请输入学生的姓名 年龄 性别 学号:"); scanf("%s %d %c %d",zhaoliu.name,&zhaoliu.age,&zhaoliu.sex,&zhaoliu.student_number); printf("参加比赛的学生有%s\t年龄:%d\t性别:%c\t学号:%d\n",zhaoliu.name,zhaoliu.age,zhaoliu.sex,zhaoliu.student_number); return 0; }
-
不指定结构体名而直接定义。
struct { 类型名 成员表列1 类型名 成员表列2 ··· }变量名表列;
代码示例:
#include <stdio.h> int main() { struct { char name[30]; int age; char sex; int student_number; }lisi,zhaoliu; printf("请输入学生的姓名 年龄 性别 学号:"); scanf("%s %d %c %d",lisi.name,&lisi.age,&lisi.sex,&lisi.student_number); printf("参加比赛的学生有%s\t年龄:%d\t性别:%c\t学号:%d\n",lisi.name,lisi.age,lisi.sex,lisi.student_number); printf("请输入学生的姓名 年龄 性别 学号:"); scanf("%s %d %c %d",zhaoliu.name,&zhaoliu.age,&zhaoliu.sex,&zhaoliu.student_number); printf("参加比赛的学生有%s\t年龄:%d\t性别:%c\t学号:%d\n",zhaoliu.name,zhaoliu.age,zhaoliu.sex,zhaoliu.student_number); return 0; }
-
-
结构体变量的使用:
- 使用结构体变量成员的方法:
结构体变量名.成员名
- 当成员又是一个结构体时,使用对应结构体的方法:
结构体变量名.成员名.成员名
- 当在输入
.
时,visual C++能自动识别出对应结构体中所包含的成员 - 同类的结构体变量可以互相赋值,例如
lisi = zhaoliu
- 使用结构体变量成员的方法:
第二节 结构体数组
-
可以定义int、char、float等类型的数组;也可以为结构体定义数组,表示一次性开辟多个结构体空间。
-
结构体数组定义的方法:
-
创建的结构体类型之后,再定义结构体数组:
结构体类型 数组名[数组个数]
代码示例:
#include <stdio.h> struct test { char name[30]; int age; char sex; int student_number; }; int main() { struct test class_five[10]; //定义了一个test类型的结构体数组,数组名为class_five,包含10个结构体 return 0; }
-
创建结构体类型的同时定义结构体数组:
struct 结构体名 { 类型名 成员表列1 类型名 成员表列2 ··· }变量名表列;
代码示例:
#include <stdio.h> int main() { struct test { char name[30]; int age; char sex; int student_number; }class_five[10]; //定义了一个test类型的结构体数组,数组名为class_five,包含10个结构体 return 0; }
实例:投票系统,为无忧村选举1名村长,预备村长有张三、李四、王五,村中一共有10名村民
#include <string.h> #include <stdio.h> int main() { struct piaoshu { char name[3]; int number; }ybcz[3] = {"zs",0,"ls",0,"ww",0}; // ybcz[0].name = "zs",ybcz[0].number =0;ybcz[1].name = "ls",ybcz[0].number =0;ybcz[0].name = "ww",ybcz[0].number =0; int i,j ; char name[3]; for(i = 0;i <10;i++) { printf("请投票:"); scanf("%s",name); for(j = 0 ; j <3; j++) if(strcmp(ybcz[j].name, name) == 0) ybcz[j].number++; } for(i = 0;i<3;i++) printf("村名:%s\t的票数是:%d\n",ybcz[i].name,ybcz[i].number); return 0; }
-
第三节 结构体指针
-
什么是结构体指针?
一个结构体变量的起始地址就是结构体变量的指针。用一个指针变量存储结构体指针就是一个结构体指针变量。
struct 结构体名 { 类型名 成员表列1; 类型名 成员表列2; ··· }lisi;
-
指针指向结构体变量的使用
指向结构体成员的方法:
(*p).name (*p).age p -> name p -> age p -> student_number
其中
*p
表达指向对应地址的内容,(*p)
加上括号是由于.
的优先级高于*
。->
表示指向运算符。代码示例:
#include <string.h> #include <stdio.h> int main() { struct test { char name[30]; int age; char sex; int student_number; }lisi ={"lisi",32,'M',20190305}; struct test *p; p = &lisi; strcpy((*p).name,"caojiu"); (*p).age = 28; printf("姓名:%s\t年龄:%d\t性别%d\t学号:%d\n",p->name, p->age,p->sex,p->student_number); return 0; }
-
指针指向结构体数组
struct test { 类型名 成员表列1; 类型名 成员表列2; ··· }student[3];
代码示例:
#include <stdio.h> int main() { struct test { char name[30]; int age; char sex; int student_number; }student[3]; struct test *p; for(p = student;p< student+3; p++) { printf("请输入姓名 年龄 性别 学号:"); scanf("%s %d %c %d",p->name,&(p->age),&(p->sex),&(p->student_number)); } for(p = student;p< student+3;p++) printf("姓名:%s\t年龄:%d\t性别%d\t学号:%d\n",p->name, p->age,p->sex,p->student_number); return 0; }
-
结构体与函数传递
- 结构体中的成员作为实参传递,和变量的使用方法一致;
- 结构体变量作为实参传递,由于形参接收实参时,也会在内存中开辟一个行同大小的空间,并且形参内容改变时,对应实参的内容不会被改变,因此这种传递方式不仅浪费空间,也不利于操作,一般不使用;
- 当结构体指针变量作为实参传递时,将指针地址赋值给形参。
代码示例:
#include <string.h> #iunclude <stdio.h> struct test { char name[30]; int age; char sex; int student_number; }; void change_info(char name[30],struct test *q) { printf("修改%s的信息\n",name); printf("修改为姓名 年龄 性别 学号:"); scanf("%s %d %c %d",q->name,&(q->age),&(q->sex),&(q->student_number)); } int main() { struct test lisi, *p = &lisi; strcpy(lisi.name,"lisi"); change_info(lisi.name,p ); printf("姓名:%s\t年龄:%d\t性别%d\t学号:%d\n",p->name, p->age,p->sex,p->student_number); return 0; }
第四节 按需分配的空间
创建一个数据,在以前使用int、char、数组、结构体等来创建都是必须在使用之前给出固定的空间长度,但是大部分数据在使用之前是没有办法确定的,例如统计每天上网的网名,这一类的人数是一个动态的数据。
-
如何创建一个按需分配的空间?
链表
可以实现存储空间动态分配的一种结构。它不是数据,而是通过代码人为的创建出一种空间动态分配方法。NULL表示指针的内容为空,void表示数据类型为空。
- 由一个“头指针”变量存放链表中的第一个表;
- 每一个表包含实际的数据和下一个表的地址;
- 每个表的存储地址都是由系统分配的不连续的存储空间。
代码示例:
struct test { char name[30]; int age; int student_number; struct test *next; //指向struct test类型数据的指针。 };
-
创建一个简单链表
代码示例:
#include <stdio.h> #include <string.h> #include <stdlib.h> struct test { char name[30]; int age; int student_number; struct test *next; //指向struct test类型数据的指针。 }; int main() { struct test a,b,c, *head,*p; strcpy(a.name,"zhangsan"); //给各个结构体赋值 a.age = 16; a.student_number = 1001; strcpy(b.name,"lisi"); b.age = 19; b.student_number = 1102; strcpy(c.name,"wangwu"); c.age = 18; c.student_number = 1009; head =&a; //把3个结构体构建成链表 a.next = &b; b.next = &c; c.next = NULL; p = head; while(p!= NULL) //输出链表内容 { printf("%d\n",p); printf("姓名:%s\t年龄:%d\t学号:%d\n",p->name,p->age,p->student_number); p = p->next; } return 0; }
-
创建动态链表
代码示例:
#include <stdio.h> #include <string.h> #include <stdlib.h> struct test { char name[30]; int age; int student_number; struct test *next; //指向struct test类型数据的指针。 }; int main() { struct test *head, *p1,*p2; int n = 0; p1 = p2 = malloc(sizeof(struct test)); //添加第一个表的内容 printf("请输入姓名 年龄 学号:"); scanf("%s %d %d",p1->name,&p1->age,&p1->student_number); head = NULL; while(p1->student_number > 0) { n = n+1; // n =3 if(n == 1) head = p1; //创建链表 else p2->next = p1; p2 = p1; p1 = malloc(sizeof(struct test)); //循环添加表的内容 printf("请输入姓名 年龄 学号:"); scanf("%s %d %d",p1->name,&p1->age,&p1->student_number); } p2->next = NULL; p1 = head; while(p1 != NULL) //检查链表是否有内容,如果有内容就打印输出 { printf("姓名:%s\t年龄:%d\t学号:%d\n",p1->name,p1->age,p1->student_number); p1 = p1->next; } return 0; }
第五节 共用体
-
什么是共用体?
多种不同的变量共用同一段内存的结构,称为共用体的结构。
例如在内存中开辟了4个字节的空间,可以用来存储int、float、char型数据,构建一种数据类型,使得开辟的空间可以存放几种不同类型的数据,但在使用的一瞬间只能存放一个数据,而不是同时存放多个数据。
-
创建共用体类型:
union 共用体名 { 类型名 成员表列1; 类型名 成员表列2; ··· 类型名 成员表列n; };
-
定义共用体变量的方法:
union 共用体名 变量名
-
在定义之前已经创建好共用体类型时,结构体变量所占内存大小是成员中占内存最大的那个;
-
union 共用体名 { 类型名 成员表列1; 类型名 成员表列2; ··· 类型名 成员表列n; }
代码示例:
#include <stdio.h> union info { int a; char b; float c; }; int main() { union info x; //占内存4字节 x.a = 100; printf("%d\t%c\t%f\n",x.a,x.b,x.c); return 0; }
-
-
共用体变量的使用方法:
注:使用共用体之前需要定义。
例如:定义了共同体变量x
x.a //引用共用体变量中的整型变量a x.b //引用共用体变量中的字符变量b x.c //引用共用体变量中的浮点型变量c
说明:
1. 共用体初始化时,只能存储一个数据,不能同时存储多个数据; 2. 对共用体变量非同时的进行多次赋值在,则共用体变量中的数据是最近一次赋值的数据; 3. 共用体的成员都使用相同的地址; 4. 同类型的共用体变量可以相互赋值 `y = x;`
代码示例:
#include <stdio.h> int main() { union info { int a; char b; float c; }x={100}; x.c = 3.14f; x.b = 'y'; //ASCII码后部分是成员c遗留的 printf("%d\t%c\t%f\n",x.a,x.b,x.c); printf("%d\t%d\t%d\n",&x.a,&x.b,&x.c); return 0; }
实例:填写公司员工信息
#include <stdio.h> struct info //一般配合struct,上节课的链表一般在处理大型数据的时候使用 { char name[20]; int age; char work; union { int gzsc; char xueli[10]; }fen; }; int main() { struct info a[2]; int i; for(i = 0; i <2; ++i) { printf("请填写入职信息:姓名 年龄 职务[c表示程序员,s表示设计师]\n"); scanf("%s %d %c",a[i].name,&a[i].age,&a[i].work); if(a[i].work == 's') { printf("请输入您工作时长:"); scanf("%d",&a[i].fen.gzsc); } else { printf("请输入您的学历:"); scanf("%s",a[i].fen.xueli); } } for(i = 0; i <2 ; ++i) { if(a[i].work == 's') printf("姓名:%s\t年龄%d\t职务:%c\t工作时长:%d\n",a[i].name,a[i].age,a[i].work,a[i].fen.gzsc); else printf("姓名:%s\t年龄%d\t职务:%c\t学历:%s\n",a[i].name,a[i].age,a[i].work,a[i].fen.xueli); } return 0; }
-
第六节 枚举
如果一个变量只有几种可能的值,则可以使用枚举
的方法定义数据类型。
-
什么是枚举
枚举是将一个变量所有可能的值一一列举出来,对应的值只能是列举出来的值中的一个,例如猜丁壳的游戏里面,只有石头剪刀布这三种。
-
如何构建枚举类型
enum 枚举类型名 { 值1, 值2, ···, 值n } //enum cdk{shitou, jiandao, bu}
-
定义枚举变量
enum cdk a, b //其中a和b分别为cdk类型的数据,且a,b的值只能是shitou, jiandao, bu
说明:枚举元素(shitou, jiandao, bu)都代表一个整数,元素值的默认顺序为0,1,2,3, ···,也可以人为修改元素值。
第七节 替换类型名
可以使用typedef
将已有的类型名进行替换。
-
typedef
替换类型名的方式:按定义变量的方式,将变量名替换为需要的类型名,并在最开头写上typedef
。typedef int ii[100]; //int a[100];创建一个类型代表数组 typedef char cc[30]; //char s[30]; typedef char *zhi; //char *p; ii a; cc s; zhi p;