第7章 结构体、联合体和枚举类型 本章导读 本章主要知识点 《 C语言程序设计》 (Visual C++ 6.0环境) 第7章 结构体、联合体和枚举类型 《 C语言程序设计》 (Visual C++ 6.0环境) 本章导读 结构体、联合体、枚举类型都是用户自己定义的数据类型,这些类型的数据是用户根据实际需要来组织的。结构体和联合体都是构造类型的数据,枚举类型是基本类型的数据。 本章主要知识点 (1)结构体类型数据的定义,结构变量的说明及引用方法 结构指针的定义、使用以及结构指针在C程序中的应用——链表的建立、输出、删除与插入等操作 (2)联合体类型数据的定义,联合变量的说明及引用方法 (3)枚举类型数据的定义,枚举变量的说明及引用方法 (4)了解自定义类型的概念和类型定义方法及应用 返回本书目录
第7章 结构体、联合体和枚举类型 7.1 结构体 7.2 联合体 7.3 枚举类型 7.4 自定义类型 7.5 综合实训 第7章 结构体、联合体和枚举类型 《 C语言程序设计》 (Visual C++ 6.0环境) 7.1 结构体 7.2 联合体 7.3 枚举类型 7.4 自定义类型 7.5 综合实训 返回本章导读
7.1 结构体 7.1.1 结构体与结构变量 7.1.2 结构成员的引用 7.1.3 结构数组 7.1.4 结构指针与链表 7.1 结构体 《 C语言程序设计》 (Visual C++ 6.0环境) 7.1.1 结构体与结构变量 7.1.2 结构成员的引用 7.1.3 结构数组 7.1.4 结构指针与链表 返回本章目录
7.1.1 结构体与结构变量 1.结构体类型的声明 2.结构变量的定义 3.结构体类型与结构变量 7.1.1 结构体与结构变量 《 C语言程序设计》 (Visual C++ 6.0环境) “结构体”是一种构造类型,是由数目固定,类型相同或不同的若干有序变量组成的集合。组成结构体的每个数据都称为结构体的“成员”,或称“分量”。 1.结构体类型的声明 2.结构变量的定义 3.结构体类型与结构变量 返回本节目录 返回本节目录
1.结构体类型的声明 《 C语言程序设计》 (Visual C++ 6.0环境) 声明一个结构体类型的一般形式为: struct <结构体名(也称为结构体标记)> { 成员表列(也称为域表) }; 应注意在括号后的分号是不可少的。成员表列由若干个成员组成,每个成员都是该结构体的一个组成部分。对每个成员也必须作类型说明,其形式为: <类型说明符> <成员名>; 成员名的命名应符合C语言标识符的书写规定,结构体成员名可与程序中其它变量同名,互不干扰。 返回7.1.1目录
2.结构变量的定义 《 C语言程序设计》 (Visual C++ 6.0环境) 对于已经声明的结构体类型,就可有对应于该类型的“结构类型变量”,简称为“结构变量”。定义结构变量有以下三种方法: (1)先声明结构体类型,再定义结构变量 即在先前声明的结构体类型后加上以下形式: struct <结构体名> <结构变量表列>; (2)在声明结构体类型的同时定义结构变量 即被定义的结构变量直接在结构体类型声明的“}”后给出。一般形式为: struct <结构体名> { 〈成员表列〉 }〈结构变量表列>; (3)直接定义结构变量 即省略结构体名。一般形式为: struct { <成员表列> }<结构变量表列>; 《 C语言程序设计》 (Visual C++ 6.0环境) 返回7.1.1目录
3.结构体类型与结构变量 《 C语言程序设计》 (Visual C++ 6.0环境) 结构体类型与结构变量的最大区别在于:结构变量占有一定的内存空间,而结构体类型只是一种数据类型的结构描述,并不占用内存空间。 struct box { float length; float width; float height; }; 它表明struct box结构体类型由大括号中所列的一些数据项组成,共需占用4x3=12个字节。 在此之后,若进行结构变量的定义如: struct box box1; 表明box1为struct box结构体类型变量,它占用了12个字节的内存单元。 《 C语言程序设计》 (Visual C++ 6.0环境) 返回7.1.1目录
7.1.2 结构成员的引用 【例7.1】 《 C语言程序设计》 (Visual C++ 6.0环境) 7.1.2 结构成员的引用 《 C语言程序设计》 (Visual C++ 6.0环境) 对结构变量的访问是通过对结构变量各个成员的访问来进行的,结构变量成员描述为:<结构变量名>.<成员名> 在引用中应遵循以下原则: (1)如果成员本身又是一个结构体类型时,则必须逐级找到最低一级的成员才能使用。 (2)对成员变量的使用与普通变量完全相同,可根据其类型进行相应的运算。 (3)可以引用成员的地址,也可以引用结构体变量的地址。 (4)允许将一个结构变量直接赋值给另一个具有相同结构的结构变量。 【例7.1】 返回本节目录
例7.1(1) 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.1】利用结构体对三名学生的学号、姓名、性别、成绩进行输入与输出。程序名为l7_1.cpp。 #include "stdio.h" struct stu { long int num; char name[20]; char sex; float score; }student1,student2={960002,"Zhang hong li",'W',98},student3;/*对student2初始化*/ main() { student1.num=960001; /*对student1各成员赋值*/ scanf("%s",student1.name); student1.sex='M'; student1.score=65; student3=student1; /*对student3直接赋值*/ 《 C语言程序设计》 (Visual C++ 6.0环境) 返回7.1.2目录
例7.1(2接上页) 《 C语言程序设计》 (Visual C++ 6.0环境) printf("\n%ld\t%20s\t%c\t%f",student1.num,student1.name,student1.sex, student1.score); printf("\n%ld\t%20s\t%c\t%f",student2.num,student2.name,student2.sex, student2.score); printf("\n%ld\t%20s\t%c\t%f",student3.num,student3.name,student3.sex, student3.score); } 运行情况如下: 输入:Lilin< CR > 960001 Lilin M 65 960002 Zhang hong li W 98 960001 Lilin M 65 《 C语言程序设计》 (Visual C++ 6.0环境) 程序演示 返回7.1.2目录
[例7.1]程序演示 返回例题 返回7.1.2目录
7.1.3 结构数组 1.结构数组的定义 2. 结构数组的初始化 【例7.2】 《 C语言程序设计》 (Visual C++ 6.0环境) 7.1.3 结构数组 《 C语言程序设计》 (Visual C++ 6.0环境) 数组元素是结构体类型数据,这时的数组就是结构数组。 1.结构数组的定义 2. 结构数组的初始化 【例7.2】 返回本节目录
1.结构数组的定义 《 C语言程序设计》 (Visual C++ 6.0环境) 结构数组的定义方法和结构变量相似,只需说明它为数组类型即可。例如: struct stu { int num; char name[20]; char sex; float score; }; struct stu student[3]; 也可以直接定义一个结构数组或省略结构体名。 返回7.1.3目录
2.结构数组的初始化 《 C语言程序设计》 (Visual C++ 6.0环境) 对结构数组可以进行初始化,它的作用在于把成批的数据传递给结构数组中的各个元素。 初始化的一般形式为: struct <结构体名> <结构数组>[n]={<初始表列>}; 其中,n为元素的个数。在初始表列中,以一个元素内容为单位,用一对“{}”括起来,各元素之间用“,”分隔。 当对数组中的全部元素初始化时,也可将“[ ]”中的数组元素个数省略。在编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。 数组的初始化也可以先声明结构体类型,然后再定义数组为该结构体类型,并在定义数组时初始化。 返回7.1.3目录
【例7.2】(1) 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.2】计算学生的平均成绩并统计出不及格的人数。程序名为l7_2.cpp。 #include "stdio.h" struct stu { long int num; char name[20]; char sex; float score; }student[3]={{200001,"Li li",'W',99},{200002,"Wang hai",'M',85}, {200003,"Liu ying",'W',50}}; main() { int i,n; float average,sum; n=0; sum=0; 《 C语言程序设计》 (Visual C++ 6.0环境) 返回7.1.3目录
【例7.2】(2上接1) 《 C语言程序设计》 (Visual C++ 6.0环境) for(i=0;i<3;i++) { sum+=student[i].score; if(student[i].score<60) n+=1; } printf("sum=%f\n",sum); average=sum/3; printf("average=%f\ncount=%d\n",average,n); 运行情况如下: sum=234.000000 average=78.000000 count=1. 《 C语言程序设计》 (Visual C++ 6.0环境) 程序演示 返回7.1.3目录
【例7.2】程序演示 返回例题 返回7.1.3目录
7.1.4 结构指针与链表 《 C语言程序设计》 (Visual C++ 6.0环境) 1.结构指针 2.链表 返回本节目录
1.结构指针 《 C语言程序设计》 (Visual C++ 6.0环境) 我们把指向结构体的指针称为结构指针,它是一个指针变量。结构指针变量说明的一般形式为: struct <结构体名> *<结构指针变量名>; 这样说明的含义是:一规定了指针的数据特性;二为结构指针本身分配了一定的内存空间。 结构指针变量必须要先赋值后才能使用,赋的值应是一个地址值。 有了结构指针变量,就能更方便地访问结构变量的各个成员。 其访问的一般形式为: <(*结构指针变量)>.<成员名> 或为: <结构指针变量>-><成员名> 《 C语言程序设计》 (Visual C++ 6.0环境) 返回7.1.4目录
1.结构指针 (1)指向结构变量 (2)指向结构数组 (3)结构指针作函数参数 《 C语言程序设计》 (Visual C++ 6.0环境) 在结构指针的使用中可分为以下几种情况: 《 C语言程序设计》 (Visual C++ 6.0环境) (1)指向结构变量 (2)指向结构数组 (3)结构指针作函数参数 返回7.1.4目录
(1)指向结构变量 【例7.3】 《 C语言程序设计》 (Visual C++ 6.0环境) 当使用结构指针指向一个结构变量时,指针变量中的值就是所指向的结构变量的首地址。 这三种用于表示结构成员的形式是完全等效的。 ①结构变量.成员名 ②(*结构指针变量).成员名 ③结构指针变量->成员名 请注意分析下面几种运算: s->n 得到s指向的结构变量中的成员n的值 s->n++ 得到s指向的结构变量中的成员n的值,用完该值后使 它加1 ++s->n 得到s指向的结构变量中的成员n的值使之加1 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.3】 返回1目录
【例7.3】(1) 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.3】通过结构指针引用结构体成员。程序名为l7_3.cpp。 #include "stdio.h" struct stu { int num; char name[20]; char sex; float score; }student1={102,"Zhang ping",'M',78.5},*s; main() { s=&student1; /*给结构指针变量赋值*/ printf("Number=%d\tName=%s\t",student1.num, student1.name); printf("Sex=%c\tScore=%f\n", student1.sex, student1.score); printf("Number=%d\tName=%s\t",(*s).num,(*s).name); printf("Sex=%c\tScore=%f\n",(*s).sex,(*s).score); printf("Number=%d\tName=%s\t",s->num,s->name); printf("Sex=%c\tScore=%f\n",s->sex,s->score); } 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(1)
【例7.3】(2上接1) 《 C语言程序设计》 (Visual C++ 6.0环境) 运行情况如下: Number=102 Name=Zhang ping Sex=M Score=78.500000 《 C语言程序设计》 (Visual C++ 6.0环境) 程序演示 返回(1)
【例7.3】程序演示 返回例题 返回(1)
(2)指向结构数组 【例7.4】 《 C语言程序设计》 (Visual C++ 6.0环境) 当结构指针指向一个结构数组时,该指针变量的值是整个结构数组的首地址。 当然结构指针也可以指向结构数组中的某个元素,这时指针变量的值是该结构数组元素的首地址。 设s为指向结构数组的指针变量,则s也指向该结构数组的0号元素,s+1指向1号元素,s+i则指向i号元素。 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.4】 返回1目录
【例7.4】(1) 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.4】用指针变量输出结构数组。程序名为l7_4.cpp。 #include "stdio.h" struct stu { long int num; char name[20]; char sex; float score; }student[3]={{200001,"Li li",'W',99},{200002,"Wang hai",'M',85}, {200003,"Liuying ",'W',50}}; main() { struct stu *s; printf("Num\tName\t\tSex\tScore\t\n"); for(s=student;s<student+3;s++) printf("%ld\t%-10s\t%c\t%f\t\n",s->num,s->name,s->sex,s->score); } 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)
【例7.4】(2上接1) 《 C语言程序设计》 (Visual C++ 6.0环境) 运行情况如下: Num Name Sex Score 200001 Li li W 99.000000 200002 Wang hai M 85.000000 200003 Liuying W 50.000000. 《 C语言程序设计》 (Visual C++ 6.0环境) 程序演示 返回(2)
【例7.4】程序演示 返回例题 返回(2)
(3)结构指针作函数参数 【例7.5】 《 C语言程序设计》 (Visual C++ 6.0环境) 使用结构指针,即用指向结构变量(或数组)的结构指针作函数参数进行传送,这时由实参向形参传递的是地址,属于“地址传递”方式,减少了时间和空间上的开销。 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.5】 返回1目录
【例7.5】(1) 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.5】用结构指针变量作函数参数编程,计算一组学生的平均成绩并统计出不及格人数。程序名为l7_5.cpp。 #include "stdio.h" struct stu { long int num; char name[20]; char sex; float score; }student[3]={{200001,"Li li",'W',99},{200002,"Wang hai",'M',85}, {200003,"Liuying ",'W',50}}; void average(struct stu *ps) { int n=0,i; float ave,s=0; 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(3)
【例7.5】(2上接1) 《 C语言程序设计》 (Visual C++ 6.0环境) for(i=0;i<3;i++,ps++) { s+=ps->score; if(ps->score<60) n+=1; } printf("s=%f\n",s); ave=s/3; printf("average=%f\ncount=%d\n",ave,n); main() { struct stu *s; s=student; average(s); 运行情况如下: s=234.000000 average=78.000000 count=1. 《 C语言程序设计》 (Visual C++ 6.0环境) 程序演示 返回(3)
【例7.5】程序演示 返回例题 返回(3)
2.链表 《 C语言程序设计》 (Visual C++ 6.0环境) (1)动态存储分配函数 (2)链表的使用 返回7.1.4目录
(1)动态存储分配函数 ①分配内存空间函数malloc ②分配内存空间函数calloc ③释放内存空间函数free 【例7.6】 《 C语言程序设计》 (Visual C++ 6.0环境) ①分配内存空间函数malloc ②分配内存空间函数calloc ③释放内存空间函数free 【例7.6】 返回2目录
①分配内存空间函数malloc 《 C语言程序设计》 (Visual C++ 6.0环境) ①分配内存空间函数malloc 调用形式:(类型说明符*) malloc (size) 功能:在内存的动态存储区中分配一块长度为“size”字节的连续空间。函数的返回值为该空间的首地址;若此函数未能成功的执行,则返回的值为0。 “类型说明符”表示把该区域用于何种数据类型。(类型说明符*)表示把返回值强制转换为该类型指针。“size”是一个无符号数。 例如:pc=(char *) malloc (100);表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量pc。 返回(1)目录
②分配内存空间函数calloc 《 C语言程序设计》 (Visual C++ 6.0环境) 调用形式:(类型说明符*) calloc(n,size) 功能:在内存动态存储区中分配n块长度为“size”字节的连续空间。函数的返回值为该空间的首地址;若此函数未能成功的执行,则返回的值为0。 (类型说明符*)用于强制类型转换。calloc函数与malloc函数的区别仅在于一次可以分配一块还是n块区域。 例如:语句ps=(struct stu*) calloc(2,sizeof(struct stu));中的函数sizeof(struct stu)是求struct stu结构体类型的长度。因此该语句的意思是:按照struct stu的长度分配2块连续区域,强制转换为struct stu类型,并把其首地址赋予指针变量ps。 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(1)目录
③释放内存空间函数free 《 C语言程序设计》 (Visual C++ 6.0环境) ③释放内存空间函数free 调用形式:free (void*ptr); 功能:释放ptr所指向的内存空间,ptr指向被释放区域的首地址,被释放区应是由malloc或calloc函数所分配的空间。ptr是一个无类型的指针变量。 返回(1)目录
【例7.6】(1) 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.6】分配一块区域,输入一个学生数据。程序名为l7_6.cpp。 #include "stdio.h" #include "malloc.h" #include "string.h" main() { struct stu { int num; char name[20]; char sex; float score; }*s; s=(struct stu*)malloc(sizeof(struct stu)); 《 C语言程序设计》 (Visual C++ 6.0环境) 返回本节目录
【例7.6】(2上接1) 《 C语言程序设计》 (Visual C++ 6.0环境) s->num=102; strcpy(s->name,"Zhang ping"); s->sex='M'; s->score=62.5; printf("Number=%d\nName=%s\n",s->num,s->name); printf("Sex=%c\nScore=%f\n",s->sex,s->score); free(s); } 运行情况如下: Num=102 Name=Zhang ping Sex=M Score=62.500000 《 C语言程序设计》 (Visual C++ 6.0环境) 程序演示 返回(1)目录
【例7.6】程序演示 返回例题 返回(1)目录
(2)链表的使用 《 C语言程序设计》 (Visual C++ 6.0环境) 链表是一种常见的、重要的数据结构,它采用动态的分配办法为一个结构体分配内存空间。 一方面需要时就分配一块空间用来存放,从而节约了宝贵的内存资源;且便于删除与加入。 另一方面,在动态分配时,每个结点之间可以是不连续的(结点内是连续的),结点之间的联系是通过指针来实现的,即在结点结构中定义一个成员项用来存放下一结点的首地址,这个用于存放地址的成员,常把它称为指针域。可在第一个结点的指针域内存入第二个结点的首地址,在第二个结点的指针域内又存放第三个结点的首地址,如此串连下去直到最后一个结点。最后一个结点因无后续结点连接,其指针域可赋为NULL。 这样一种连接方式,如同一条一环接一环的链子,在数据结构中称之为“链表”。 《 C语言程序设计》 (Visual C++ 6.0环境) 返回2目录
(2)链表的使用 《 C语言程序设计》 (Visual C++ 6.0环境) ①建立链表 ②链表的输出 ③链表的删除操作 ④链表的插入操作 例如,一个存放学生学号和成绩的结点可以定义为以下结构: struct stu { int num; int score; struct stu *next; }; 在该结构体中前两个成员项组成数据域,最后一个成员项next构成指针域,它是一个指向struct stu类型的结构指针变量。 对链表的基本操作主要有: 《 C语言程序设计》 (Visual C++ 6.0环境) ①建立链表 ②链表的输出 ③链表的删除操作 ④链表的插入操作 【例7.11】 返回2目录
①建立链表 (1) 《 C语言程序设计》 (Visual C++ 6.0环境) 建立链表就是指从无到有地建立起一个链表,即一个个地输入各结点数据,并建立起前后相链的关系。 【例7.7】编写一个建立单向链表的函数,存放学生数据。 函数create可编写如下: #include "stdio.h" #include "malloc.h" #define NULL 0 /*令NULL为0,用它表示空地址*/ #define LEN sizeof (struct stu)/*LEN代表struct stu结构体类型数据的长度*/ struct stu { long int num; float score; struct stu *next; }; 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
①建立链表 (2上接1) 《 C语言程序设计》 (Visual C++ 6.0环境) int n; struct stu *creat() /*此函数带回一个指向链表头的指针*/ { struct stu *head,*p1,*p2; n=0; /*n为结点的个数*/ p1=p2=(struct stu *)malloc(LEN); /*开辟一个新单元*/ scanf("%ld,%f",&p1->num,&p1->score); head=NULL; while(p1->num!=0) { n=n+1; if (n==1)head=p1; else p2->next=p1; 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
①建立链表(3上接2) 《 C语言程序设计》 (Visual C++ 6.0环境) p2=p1; p1=( struct stu *)malloc(LEN); scanf("%ld,%f",&p1->num,&p1->score); } p2->next=NULL; return(head); /*返回链表的头地址*/ 图7-7、图7-8、图7-9、图7-10、图7-11表示出creat函数的执行过程。 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
图7-7 返回
图7-8 返回
图7-9(a) 返回
图7-9(b) 返回
图7-9(c) 返回
图7-10(a) 返回
图7-10(b) 返回
图7-10(c) 返回
图7-11 返回
②链表的输出 《 C语言程序设计》 (Visual C++ 6.0环境) 将链表中各结点的数据依次输出,首先要知道链表头元素的地址。设一个指针变量p,指向第一个结点,待输出p所指的结点数据后,使p后移一个结点再输出,直至链表的尾结点止。程序执行过程可见图7-12所示。 【例7.8】写一个函数,输出链表中所有结点。 void print(head) /*由实参即将已有的链表的头指针传给被调函数*/ struct stu *head; { struct student *p; printf("\n Now ,these %d records are:\n",n); p=head; /* p指向头结点*/ while(p!=NULL) { printf("%ld,%f\n",p->num,p->score); /*输出所指结点的数据域*/ p=p->next; /*使p指向下一个结点*/ } 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
图7-12 返回
③链表的删除操作 《 C语言程序设计》 (Visual C++ 6.0环境) 从一个链表中删除一个结点,并不是真正从内存中把它抹去,而是把它从链表中分离开来,可以通过改变链表的链接关系完成。 分析:设两个指针变量p1和p2,先使p1指向第一个结点。删除一个结点有两种情况: 一种情况是要删除结点是第一个结点,此时只需使head指向第二个结点即可,即head=p1->next,其过程如图7-13所示。 另一种情况是被删除结点不是第一个结点,可使被删除结点的前一结点指向被删结点的后一结点,即p2->next=p1->next,其过程如图7-14所示。 【例7.9】写一个函数,删除链表中的指定结点,以指定的学号作为删除结点的标志。函数dele编写如下: struct stu * dele(struct stu *head, long int num) { struct stu *p1,*p2; 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
③链表的删除操作 《 C语言程序设计》 (Visual C++ 6.0环境) if(head==NULL) /*如为空表, 输出提示信息*/ { printf("\nempty list!\n"); goto end;} p1=head; while (p1->num!=num && p1->next!=NULL) /*当不是要删除的结点,而且也不是最后一个结点时,继续循环*/ { p2=p1;p1=p1->next; /*后移一个结点*/ } if(p1->num==num) /*找到要删除的结点*/ { if(p1==head)head=p1->next; /*为第一结点head指向第二结点*/ else p2->next=p1->next; /*不是第一个结点,使要删除结点从链表中脱离*/ 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
③链表的删除操作 《 C语言程序设计》 (Visual C++ 6.0环境) n=n-1; free(p1); } else printf("The node not been foud!\n"); end: return head; /*返回head值*/ 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
图7-13 返回
图7-14(a) 返回
图7-14(b) 返回
图7-14(c) 返回
④链表的插入操作 《 C语言程序设计》 (Visual C++ 6.0环境) 在一个链表的指定位置插入结点,首先要求链表本身必须是已按某种规律排好序的。按照排序的规律确定好新结点在链表中的位置,并将其连入链表中即可。分析:设被插入结点的指针为p0,p1指向第一个结点。可在四种不同情况下插入: 第一种情况是原链表是空链表,只需使head指向被插入结点即可,见图7-15(a)。 第二种情况是被插入结点值最小,则应插入到第一个结点之前。这种情况下使head指向被插入结点,被插入结点的指针域指向原来的第一个结点即可,即用p0->next=p1;head=p0;完成,见图7-15(b)。 第三种情况是在其它位置插入,见图7-15(c)。这种情况下,使插入位置的前一结点的指针域指向被插入结点,使被插入结点的指针域指向插入位置的后一结点,即用p0->next=p1;p2->next=p0;来完成。 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
④链表的插入操作 《 C语言程序设计》 (Visual C++ 6.0环境) 最后一种情况是在表末插入,见图7-15(d)。这种情况下使原链表中的末结点指针域指向被插入结点,被插入结点指针域置为NULL。即用p1->next=p0p0->next=NULL;来完成。 【例7.10】写一个函数,在学生数据链表中,按学号顺序插入一个结点。 struct stu * insert(struct stu *head, struct stu *stud) { struct stu *p0,*p1,*p2; p1=head; /*指向第一个结点*/ p0=stud; /*指向要插入的结点*/ if(head==NULL) /*空表插入*/ { head=p0; p0->next=NULL; } /*将p0指向的结点作第一个结点*/ 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
④链表的插入操作 《 C语言程序设计》 (Visual C++ 6.0环境) else { while((p0->num>p1->num)&&(p1->next!=NULL)) { p2=p1; p1=p1->next; } /*找插入位置*/ if(p0->num<=p1->num) { if(head==p1)head=p0; /*在第一结点之前插入*/ else p2->next=p0; /*在其它位置插入*/ p0->next=p1; } 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
④链表的插入操作 《 C语言程序设计》 (Visual C++ 6.0环境) else { p1->next=p0; p0->next=NULL; } /*在表末插入*/ } n=n+1; return (head); 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
图7-15(a) 返回
图7-15(b) 返回
图7-15(c) 返回
图7-15(d) 返回
【例7.11】 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.11】将以上建立链表、输出链表、删除结点、插入结点的函数组织在一个程序中,用main函数作主调函数。程序名为l7_7891011.cpp。 main函数内容如下:(其位置在以上各函数之后) #include "stdio.h" main() { struct stu * head,stud; long int num; printf("input records:\n "); head=creat(); /*调用creat函数建立链表并把头指针返回给head*/ print(head); /*调用print函数输出链表*/ printf("Input the deleted number: "); scanf(“%ld”,&num); /*输入待删结点的学号*/ head=dele(head,num); /*调用dele函数删除一个结点*/ 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
【例7.11】 《 C语言程序设计》 (Visual C++ 6.0环境) print(head); /*调用print函数输出链表*/ printf("Input the inserted number and score: "); scanf("%ld,%f",&stud.num,&stud.score); /*输入待插入结点的数据域值*/ head=insert(head,&stu); /* 调用insert函数插入pnum所指的结点*/ print(head); /*再次调用print函数输出链表*/ } 运行情况如下: input records: 输入:96001,85 < CR > 96002,75.2< CR > 96005,62< CR > 0,0< CR > 《 C语言程序设计》 (Visual C++ 6.0环境) 返回(2)目录
【例7.11】 《 C语言程序设计》 (Visual C++ 6.0环境) Now,these 3 records are: 96001,85.000000 96002,75.199997 96005,62.000000 Input the deleted number: 输入:96002 < CR > Now,these 2 records are: Input the insert number and score: 输入:96004,78 < CR > 96004,78.000000 《 C语言程序设计》 (Visual C++ 6.0环境) 程序演示 返回(2)目录
【例7.11】程序演示 返回例题 返回本节目录
7.2 联合体 7.2.1 联合体和联合变量 7.2.2 联合体的引用及特点 【例7.12】 7.2 联合体 《 C语言程序设计》 (Visual C++ 6.0环境) 在C语言中,把这种将几种不同类型的变量存放在同一段内存单元中的结构称为“联合体”,也称为“共用体”。 “联合体”与“结构体”有一些相似之处,但两者有本质上的不同。在结构体中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和;而在联合体中,各成员共享一段内存空间,一个联合变量的长度等于各成员中最长的长度。 7.2.1 联合体和联合变量 7.2.2 联合体的引用及特点 【例7.12】 返回本章目录
7.2.1 联合体和联合变量 1.联合体类型的声明 2.联合变量的定义 《 C语言程序设计》 (Visual C++ 6.0环境) 7.2.1 联合体和联合变量 《 C语言程序设计》 (Visual C++ 6.0环境) 1.联合体类型的声明 2.联合变量的定义 返回本节目录
1.联合体类型的声明 《 C语言程序设计》 (Visual C++ 6.0环境) 声明一个联合体类型的一般形式为: union <联合体名> { <成员表列> }; 成员表列中含有若干成员,成员的一般形式为: <类型说明符> <成员名>; 成员名的命名应符合标识符的规定。 例如: union perdata { int class; char office[10]; 《 C语言程序设计》 (Visual C++ 6.0环境) 返回7.2.1目录
2.联合变量的定义 《 C语言程序设计》 (Visual C++ 6.0环境) 联合体类型声明之后,就可以进行联合变量的定义,被定义为union perdata类型的变量,可以存放整型量class或字符型数组office。 联合变量的定义和结构变量的定义方式相似,也有三种形式。以union perdata联合体类型为例,定义如下: (1)先声明再定义 (2)声明的同时定义 (3)直接定义 《 C语言程序设计》 (Visual C++ 6.0环境) 返回7.2.1目录
7.2.2 联合体的引用及特点 《 C语言程序设计》 (Visual C++ 6.0环境) 1.引用 2.特点 返回本节目录
1.引用 《 C语言程序设计》 (Visual C++ 6.0环境) 对联合变量的赋值、使用都只能针对联合变量的成员进行。联合变量的成员表示为: <联合变量名>.<成员名> 例如,a被定义为union perdata类型的变量之后,可使用a.class、a.office成员。 返回7.2.2目录
2.特点 《 C语言程序设计》 (Visual C++ 6.0环境) 在使用联合体类型数据时要注意它具有以下一些特点: (1)一个联合变量,每次只能赋予一个成员值。换句话说,一个联合变量的值就是联合变量的某一个成员值。 (2)联合变量中起作用的成员是最后一次存放的成员。 (3)不允许只用联合变量名作赋值或其它操作,如a=1;或b=a;均为错。 (4)不允许对联合变量作初始化赋值,赋值只能在程序中进行。 (5)不能把联合变量作为函数参数,也不能使函数带回联合变量,但可以使用指向联合变量的指针。 (6)联合体类型可以出现在结构体类型定义中,也可以定义联合体数组。结构体也可以出现在联合体类型定义中,数组也可以作为联合体的成员。 《 C语言程序设计》 (Visual C++ 6.0环境) 返回7.2.2目录
【例7.12】 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.12】编写程序使用联合体类型数据来保存如下表格(表7-1)中的数据,调试验证是否可行。程序名为l7_12.cpp。 #include "stdio.h" main() { union t { char *name; int age; int income; }; union t list; printf("%d\n",sizeof(union t*)); /*输出联合体类型长度*/ list.name="Zhang hai"; /*第一次赋值*/ printf("%s\t",list.name); list.age=20; /*第二次赋值*/ printf("%d\t",list.age); list.income=2500; /*第三次赋值*/ printf("%d\n",list.income); printf("%s\t%d\t%d\n",list.name,list.age,list.income);/*输出各成员值*/ } 《 C语言程序设计》 (Visual C++ 6.0环境) 程序演示 返回本节目录
【例7.12】程序演示 返回例题 返回本节目录
7.3 枚举类型 7.3.1 枚举类型及变量的定义 7.3.2 枚举元素的引用 【例7.14】 7.3 枚举类型 《 C语言程序设计》 (Visual C++ 6.0环境) C语言提供了一种“枚举”类型,在枚举类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义中列举出来的常量的范围。应该说明的是,枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。 7.3.1 枚举类型及变量的定义 7.3.2 枚举元素的引用 【例7.14】 返回本章目录
7.3.1 枚举类型及变量的定义 1.枚举类型的声明 2.枚举变量的定义 《 C语言程序设计》 (Visual C++ 6.0环境) 7.3.1 枚举类型及变量的定义 《 C语言程序设计》 (Visual C++ 6.0环境) 1.枚举类型的声明 2.枚举变量的定义 返回本节目录
1.枚举类型的声明 《 C语言程序设计》 (Visual C++ 6.0环境) 枚举类型声明的一般形式为: enum <枚举类型名> { <枚举值表> }; 在枚举值表中应罗列出所有可用值,用“,”分隔,这些值也称为枚举元素或枚举常量。枚举元素是用户自己定义的标识符,并不自动代表什么含义。 例如: enum weekday { sun,mon,tue,wed,thu,fri,sat 该枚举名为weekday,枚举值共有7个,即一周中的七天。凡被声明为enum weekday类型的变量取值只能是七天中的某一天。其中的sun不一定就代表“星期天”,用什么标识符代表什么含义,完全由程序员自己决定,并在程序中做相应处理。 返回7.3.1目录
2.枚举变量的定义 《 C语言程序设计》 (Visual C++ 6.0环境) 枚举变量也有多种不同的定义方式。现有变量a、b、c需被定义为上述的enum weekday类型的变量,可采用下述任一种方式: (1)先声明后定义 enum weekday { ...... }; enum weekday a,b,c; (2)声明的同时定义 { ..... }a,b,c; (3)直接定义 enum 《 C语言程序设计》 (Visual C++ 6.0环境) 返回7.3.1目录
7.3.2 枚举元素的引用 《 C语言程序设计》 (Visual C++ 6.0环境) 枚举类型在使用中有以下规定: 7.3.2 枚举元素的引用 枚举类型在使用中有以下规定: 1.枚举元素是常量,不是变量,不能在程序中再对它赋值。 2.枚举元素本身由系统定义了一个表示序号的数值,从0开始顺序定义为0,1,2…。 3.在定义时可以改变枚举元素的值。 4.枚举值可以用来做判断比较。如:if(a>sun)……是合法的。 5.只能把枚举元素的值赋予枚举变量,不能把枚举元素的数值直接赋予枚举变量。 6.如一定要把枚举元素的数值赋予枚举变量,则必须用强制类型转换。如:a=(enum weekday)2;其意义是将顺序号为2的枚举元素赋予枚举变量a,相当于:a=tue;。 7.还应该说明的是枚举元素不是字符常量也不是字符串常量,使用时不要加单、双引号。 《 C语言程序设计》 (Visual C++ 6.0环境) 返回本节目录
【例7.14】(1) 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.14】有zhao、wang、zhang、li四人轮流值班,本月有31天,第一天由zhang来值班,编写程序做出值班表。程序名为lt7_14.cpp。 #include "stdio.h" main() { enum body { zhao,wang,zhang,li }month[31],j; int i; j=zhang; for(i=1;i<=30;i++) { month[i]=j; j=(enum body)(j+1); /*必须使用强制类型转换*/ if (j>li) j=zhao; } 《 C语言程序设计》 (Visual C++ 6.0环境) 返回本节目录
【例7.14】(2上接1) 《 C语言程序设计》 (Visual C++ 6.0环境) for(i=1;i<=30;i++) { switch(month[i]) { case zhao:printf(" %2d %6s\t",i,"zhao"); break; case wang:printf(" %2d %6s\t",i,"wang"); break; case zhang:printf(" %2d %6s\t",i,"zhang"); break; case li:printf(" %2d %6s\t",i,"li"); break; default:break; } printf("\n"); 《 C语言程序设计》 (Visual C++ 6.0环境) 程序演示 返回本节目录
【例7.14】程序演示 返回例题 返回本节目录
7.4 自定义类型 1.自定义类型的使用 2.自定义类型的作用 3.有关typedef的几点说明 7.4 自定义类型 《 C语言程序设计》 (Visual C++ 6.0环境) 1.自定义类型的使用 2.自定义类型的作用 3.有关typedef的几点说明 返回本章目录
1.自定义类型的使用 《 C语言程序设计》 (Visual C++ 6.0环境) (1)自定义类型的一般形式 typedef <原类型名> <新类型名> 其中原类型名中含有定义部分,新类型名一般用大写表示,以便于区别。 (2)自定义一个新类型名的方法 ①先按定义变量的方法写出定义体。(如:int a;) ②将变量名换成新类型名。(如:将a换成INTEGER) ③在最前面加上typedef。(如:typedef int INTEGER) 至此一个新的类型名就定义好了。 返回本节目录
2.自定义类型的作用 《 C语言程序设计》 (Visual C++ 6.0环境) (1)用typedef定义数组、指针、结构体等类型将带来很大的方便,不仅使程序书写简单而且使意义更为明确,因而增强了可读性。 例如:typedef char NAME[20];表示NAME是字符数组类型,数组长度为20。 我们可用NAME说明变量,如:NAME a1,a2,s1,s2; 完全等效于:char a1[20],a2[20],s1[20],s2[20];。 定义STU表示struct stu结构体类型,然后可用STU来说明结构变量,如: STU body1,body2; (2)使用typedef有利于程序的通用和移植。当程序依赖于硬件特性时,用typedef便于移植。 返回本节目录
3.有关typedef的几点说明 《 C语言程序设计》 (Visual C++ 6.0环境) (3)在有时也可用宏定义来代替typedef的功能,但事实上,二者是不同的。宏定义是在预编译是处理完成的,只是简单的字符串替换;而typedef则是在编译时完成的,后者更为灵活方便。 返回本节目录
7.5 综合实训 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.15】 【例7.16】 返回本章目录
【例7.15】(1) 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.15】设有一个教师与学生通用的表格(表7-2),教师数据有号码、姓名、职业、教研室四项;学生数据有号码、姓名、职业、班级四项。编写程序输入人员数据,再以表格输出。程序名为l7_15.cpp。 #include "stdio.h" struct { int num; char name[10]; char job; union { int classnum; char office[10]; } depa; }body[2]; 《 C语言程序设计》 (Visual C++ 6.0环境) 返回本节目录
【例7.15】(2上接1) 《 C语言程序设计》 (Visual C++ 6.0环境) main() { int n,i; for(i=0;i<2;i++) { printf("input num,name,job and department\n"); scanf("%d %s %c",&body[i].num,body[i].name, &body[i].job); if(body[i].job=='s') scanf("%d",&body[i].depa.classnum); else if (body[i].job=='t') scanf("%s",body[i].depa.office); else printf( "input error"); } printf("num\tname\t\tjob\t department \n"); 返回本节目录
【例7.15】(3上接2) 《 C语言程序设计》 (Visual C++ 6.0环境) for(i=0;i<2;i++) { if(body[i].job=='s') printf("%d\t%s\t%c\t%d\n",body[i].num,body[i].name,body[i].job, body[i].depa.classnum); else printf("%d\t%s\t%c\t%s\n",body[i].num,body[i].name, body[i].job, body[i].depa.office); } 返回本节目录
【例7.15】(4上接3) 《 C语言程序设计》 (Visual C++ 6.0环境) 运行情况如下: input num,name,job,and department 输入:9601 zhanghai s 302< CR > 输入:9603 lixiaoming t jiaowuke < CR > num name job department 9601 zhanghai s 302 9603 lixiaoming t jiaowuke 程序演示 返回本节目录
【例7.15】程序演示 返回例题 返回本节目录
【例7.16】(1) 《 C语言程序设计》 (Visual C++ 6.0环境) 【例7.16】口袋中有红、黄、蓝、白、黑五种颜色的棋子若干个,每次从口袋中取出3个不同颜色的棋子,问可得到多少种不同的取法,打印出每种组合的三种颜色。程序名为l7_16.cpp。 分析:采用枚举变量来处理,设取出的球为i、j、k,根据题意,它们分别是5种颜色的棋子之一,并且i≠j≠k。可以采用穷举法,看哪一种符合条件。 #include "stdio.h" main() { enum color{red,yellow,blue,white,black}i,j,k,pri; int n,loop; n=0; for(i=red;i<=black;i=(enum color)(i+1)) for(j=red;j<=black; j=(enum color)(j+1)) if(i!=j) 返回本节目录
【例7.16】(2上接1) 《 C语言程序设计》 (Visual C++ 6.0环境) { for(k=red;k<=black; k=(enum color)(k+1)) if((k!=i)&&(k!=j)) { n=n+1; printf("%-4d",n); for(loop=1;loop<=3;loop++) { switch(loop) { case 1:pri=i; break; case 2:pri=j; break; case 3:pri=k; break; default:break; } 返回本节目录
【例7.16】(3上接2) 《 C语言程序设计》 (Visual C++ 6.0环境) switch(pri) { case red:printf("%-10s","red");break; case yellow:printf("%-10s","yellow");break; case blue:printf("%-10s","blue");break; case white:printf("%-10s","white");break; case black:printf("%-10s","black");break; default:break; } printf("\n"); printf("\ntotal:%5d\n",n); 《 C语言程序设计》 (Visual C++ 6.0环境) 程序演示 返回本节目录
【例7.16】程序演示 返回例题 返回本节目录