第十一章
本章要点结构体的概念结构体的定义和引用结构体数组
主要内容
11.1 概述
11.2 定义结构体类型变量的方法
11.3 结构体变量的引用
11.4 结构体变量的初始化
11.5 结构体数组
11.6指向结构体类型数据的指针
11.7 用指针处理链表
11.8 共用体
11.9 枚举类型
11.10 用 typedef定义类型
11.1 概述
问题定义:
有时需要将不同类型的数据组合成一个有机的整体,以便于引用。 如:
一个学生有学号 /姓名 /性别 /年龄 /地址等属性
int num; char name[20]; char sex;
int age; int char addr[30];
应当把它们组织成一个组合项,在一个组合项中包含若干个类型不同(当然也可以相同)
的数据项。 图 11-1
100101 Li Fun M 18 87.5 Beijing
Num name sex age score addr
11.1 概述
声明一个结构体类型的一般形式为:
struct 结构体名
{成员表列};
如,struct student
{
int num;char name[20];char sex;
int age;float score;char addr[30];
}
结构体名类型名 成员名
11.2 定义结构体类型变量的方法
可以采取以下 3种方法定义结构体类型变量:
(1)先声明结构体类型再定义变量名例如,struct student student1,student2;
| | |
结构体类型名 结构体变量名定义了 student1和 student2为 struct student
类型的变量,即它们具有 struct student
类型的结构,图 11-2
student1
100101 ZhangXin M 19 90.5 Shanghai
100102 WangLi F 20 98 Beijing
student2
11.2 定义结构体类型变量的方法在定义了结构体变量后,系统会为之分配内存单元。
例如,student1和 student2在内存中各占 59个字节( 2+20+1+2+4+30=59)。
注意:
将一个变量定义为标准类型(基本数据类型)与定义为结构体类型不同之处在于后者不仅要求指定变量为结构体类型,而且要求指定为某一特定的结构体类型,因为可以定义出许许多多种具体的结构体类型。
11.2 定义结构体类型变量的方法
(2)在声明类型的同时定义变量这种形式的定义的一般形式为,
struct 结构体名

成员表列
} 变量名表列;
11.2 定义结构体类型变量的方法例如:
struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
} student1,student2;
它的作用与第一种方法相同,即定义了两个 struct
student 类型的变量 student1,
student2
11.2 定义结构体类型变量的方法
(3) 直接定义结构体类型变量其一般形式为,
struct

成员表列
} 变量名表列;
即不出现结构体名。
注意:
(1) 类型与变量是不同的概念,不要混同。只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。
在编译时,对类型是不分配空间的,只对变量分配空间。
(2)对结构体中的成员(
即“域”),可以单独使用,它的作用与地位相当于普通变量。
(3)成员也可以是一个结构体变量。
(4) 成员名可以与程序中的变量名相同,二者不代表同一对象。
11.2 定义结构体类型变量的方法例如,struct date /*声明一个结构体类型 */
int num;
char name[20];
char sex;
int age;
float score;
struct date birthday;
/*birthday是 struct date类型 */
char addr[30];
} student1,student2;
先声明一个 struct date
类型,它代表“日期
”,包括 3个成员:
month(月),day(
日),year(年)。
然后在声明 struct
student类型时,将成员 birthday指定为
struct date类型。
图 11-3
birthday addr
Num name sex age
Month day year
11.3结构体变量的引用
在定义了结构体变量以后,当然可以引用这个变量 。 但应遵守以下规则,
(1)不能将一个结构体变量作为一个整体进行输入和输出 。
例如,已定义 student1和 student2为结构体变量并且它们已有值 。
printf(″% d,%s,%c,%d,%f,%\ n″,student1);?
11.3结构体变量的引用引用结构体变量中成员的方式为结构体变量名,成员名例如,student1.num表示 student1变量中的
num成员,即 student1的 num(学号 )项 。 可以 对 变 量 的 成 员 赋 值,例如
:student1.num=10010;“.”是成员 (分量 )运算符,它在所有的运算符中优先级最高,因此可以把 student1.num作为一个整体来看待 。 上面赋值语句的作用是将整数 10010
赋给 student1变量中的成员 num。
11.3结构体变量的引用
(2) 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。
例如,对上面定义的结构体变量 student1,可以这样访问各成员,
student1.num
student1.birthday.month
注意:
不能用
student1.birthday
来访问 student1变量中的成员
birthday,因为
birthday本身是一个结构体变量。
11.3结构体变量的引用
(3) 对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
例如:
student2.score=student1.score;
sum=student1.score+student2.score;
student1.age++;
++student2.age;
由于“.”运算符的优先级最高,因此s
tudent1.a
ge++是对stu
dent1.age
进行自加运算,而不是先对age进行自加运算。
11.3结构体变量的引用
(4) 可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
例如:
scanf(″%d″,&student1.num);
(输入 student1.num的值)
printf(″%o″,& student1);
(输出 student1的首地址)
11.3结构体变量的引用但不能用以下语句整体读入结构体变量,
例如:
scanf( ″%d,% s,% c,% d,% f,%
s″,& student1);
结构体变量的地址主要用作函数参数,
传递结构体变量的地址。
11.4结构体变量的初始化但不能用以下语句整体读入结构体变量,
例如:
scanf( ″%d,% s,% c,% d,% f,%
s″,& student1);
结构体变量的地址主要用作函数参数,
传递结构体变量的地址。
例 11.1 对结构体变量初始化,
#include <stdio.h>
void main()
{struct student
{ long int num;
char name[20];
char sex;
char addr[20];
} a={10101,″LiLin″,′M′,″123 Beijing
Road″ }; /* 对结构体变量 a赋初值 */
printf(″No.:%ld \ nname:%s\ nsex:%c\ naddress:%s
\ n″,a.num,a.name,a.sex,a.addr); }
运行结果:
No.,10101
name,LiLin
sex:M
address,123 Beijing Road
11.5 结构体数组一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。结构体数组与以前介绍过的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。
11.5 结构体数组
11.5.1定义结构体数组和定义结构体变量的方法相仿,只需说明其为数组即可。 例如:
struct student
{int num;char name[20];char sex;int age;
float score;char addr[30];
};struct student[3];
以上定义了一个数组 stu,数组有3个元素,均为 struct
student类型数据。
11.5 结构体数组也可以直接定义一个结构体数组,例如,
struct student
{int num;
… };stu[3];
或:
strcut student
{int num;
… };stu[3];图 11-4
11.5 结构体数组
11.5.2 结构体数组的初始化与其他类型的数组一样,对结构体数组可以初始化。 例如,
struct student
{ int num;char name[20]; char sex;
int age; float score; char addr[30];
} ;stu[ 2]= {{10101,″LiLin″,
′M′,18,87.5,″103
BeijingRoad″ },{ 10102,″Zhang
Fun″,′M′,19,99,″130
Shanghai Road″}} ; 图 11-5
11.5 结构体数组当然,数组的初始化也可以用以下形式:
struct student
{ int num;

};
struct student str[]{{… },{… },{… }};
即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。
结构体数组初始化的一般形式是在定义数组的后面加上
“={初值表列};”。
11.5 结构体数组
11.5.3 结构体数组应用举例例 11.2对候选人得票的统计程序。设有 3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。
#include <string.h>
#include <stdio.h>
struct person
{
char name[20];in count;
};
leader[3]={“Li”,0,,Zhang”,0,,Fun”,0}
例 11.2
void main()
{ int i,j; char leader_name[20];
for(i=1;i<=10;i++)
{
scanf(“%s”,leader_name);
for(j=0;j<3;j++)
if(strcmp(leader_name,leader[j].name)==0)
leader[j].count++;
}
printf(,\n”);
for(i=0;i<3;i++)
printf(“%5s:%d \n”,leader[i].name,leader[i].cou
nt);}
运行结果:
Li ↙
Fun ↙
Zhang ↙
Zhang ↙

Li ↙
Fun ↙
Zhang ↙
Li ↙
Li,4
Zhang,3
Fun,3
11.5 结构体数组程序定义一个全局的结构体数组 leader,它有3个元素,每一个元素包含两个成员
name(姓名)和 count(票数)。在定义数组时使之初始化,使 3位候选人的票数都先置零。
在主函数中定义字符数组 leader-name,它代表被选人的姓名,在 10次循环中每次先输入一个被选人的具体人名,然后把它与 3个候选人姓名相比,看它和哪一个候选人的名字相同。在输入和统计结束之后,将 3人的名字和得票数输出。 图 11-6
Li 0
Zhang 0
Fun 0
name count
11.6 指向结构体类型数据的指针一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素。
11.6.1 指向结构体变量的指针下面通过一个简单例子来说明指向结构体变量的指针变量的应用。
例1 1.3指向结构体变量的指针的应用
#include <string.h>
#include <stdio.h>
void main()
{struct student{long num;char name[20];
char sex; float score;};
struct student stu_1;
struct student* p; p=&stu_1;
stu_1.num=89101;strcpy(stu_1.name,”LiLin”);
stu_1.sex=‘M’;stu_1.score=89.5;
printf(″No.:%ld \ nname:%s\ nsex:%c\ nscore:%f\ n″
,stu-1.num,stu-1.name,stu-1.sex,stu-1.score);
printf(″No.:%ld \ nname:%s\ nsex:%c\ nscore:%f\ n″
,(*p).num,(*p).name,(*p).sex,(*p).score);
}
定义指针变量 p,
指向 struct student
类型的数据
p指向的结构体变量中的成员运行结果:
No.,89101
name,LiLin
sex:M
score,89.500000
No.,89101
name,LiLin
sex:M
score,89.500000
11.6 指向结构体类型数据的指针程序分析:
在函数的执行部分将结构体变量stu -1的起始地址赋给指针变量p,也就是使p指向stu -1
,然后对stu -1的各成员赋值。第一个prin
tf函数是输出stu -1的各个成员的值。用st
u -1.num表示stu -1中的成员num,依此类推。第二个printf函数也是用来输出s
tu -1各成员的值,但使用的是( *p).num
这样的形式。
图 11-7
11.6 指向结构体类型数据的指针以下 3种形式等价:
① 结构体变量.成员名
②( *p).成员名
③ p ->成员名其中 ->称为指向运算符。请分析以下几种运算:
p ->n得到p指向的结构体变量中的成员n的值。
p ->n++ 得到p指向的结构体变量中的成员n
的值,用完该值后使它加1。
++p ->n 得到p指向的结构体变量中的成员n
的值加1,然后再使用它。
11.6 指向结构体类型数据的指针
11.6.2 指向结构体数组的指针例 11.4 指向结构体数组的指针的应用
#include <stdio.h>
struct student
{int num;char name[20];char sex;int age;};
struct student stu[3]={{ 10101,″Li Lin″,′M′,18},{
10102,″Zhang Fun″,′M′,19},{ 10104,″WangMing″,
′F′,20}} ;
void main()
{ struct student *p;
printf(″ No,Name sex age\n ″);
for (p= str;p< str+3; p++)
printf(″%5d %-20s %2c %4d\ n″,p->num,p->name,p-
>sex,p->age);
}
运行结果:
No,Name sex age
10101 LiLin M 18
10102 Zhang Fun M 19
10104 WangMing F 20
11.6 指向结构体类型数据的指针程序分析:
p是指向 struct student结构体类型数据的指针变量。在 for语句中先使p的初值为 stu,也就是数组 stu第一个元素的起始地址。在第一次循环中输出
stu[0]的各个成员值。然后执行p++,使p自加
1。p加1意味着 p所增加的值为结构体数组 stu的一个元素所占的字节数。执行p ++后 p的值等于 stu
+ 1,p指向 stu[1]。在第二次循环中输出 stu[1]的各成员值。在执行p++后,p的值等于 stu+2,再输出 stu [2]的各成员值。在执行p ++后,p的值变为
stu +3,已不再小于 stu+3了,不再执行循环。 图 11-8
11.6 指向结构体类型数据的指针注意:
(1) 如果p的初值为 stu,即指向第一个元素,
则p加1后 p就指向下一个元素。 例如,
(++p)->num 先使p自加1,然后得到它指向的元素中的 num成员值(即 10102)。
(p++)->num 先得到p ->num的值(即 10101)
,然后使p自加1,指向 stu[1]。
请注意以上二者的不同。
11.6 指向结构体类型数据的指针注意:
(2) 程序已定义了p是一个指向 struct student
类型数据的指针变量,它用来指向一个 struct
student类型的数据,不应用来指向 stu数组元素中的某一成员。
例如,p=stu[ 1].n ame ;
如果要将某一成员的地址赋给 p,可以用强制类型转换,先将成员的地址转换成 p的类型。
例如:p=(struct student *)
stu[ 0].n ame;
11.6 指向结构体类型数据的指针
11.6.3 用结构体变量和指向结构体的指针作函数参数将一个结构体变量的值传递给另一个函数,有
3个方法,
(1) 用结构体变量的成员作参数 。
(2) 用结构体变量作实参 。
(3) 用指向结构体变量 ( 或数组 ) 的指针作实参
,将结构体变量 ( 或数组 ) 的地址传给形参

11.6 指向结构体类型数据的指针
11.6.2 指向结构体数组的指针例 11.5 有一个结构体变量 stu,内含学生学号、姓名和 3
门课程的成绩。要求在 main函数中赋予值,在另一函数
print中将它们输出。今用结构体变量作函数参数。
#include <stdio.h>
struct student
{
int num;
char name[20];
float score[3];
};
11.6 指向结构体类型数据的指针
void main()
{
void print(struct student);
struct student stu;
stu.num=12345;strcpy(stu.name,
″LiLin″;stu.score[0]=67.5;stu.score[1]=89;stu.score[2]
=78.6);
print(stu);
}
void print(struct student stu)
{ printf(FORMAT,stu.num,stu.name,stu.score[0]
,stu.score[1],stu.score[2]);
printf( ″\n″); }
运行结果

12345
Li Li
67.500000
89.000000
78.599998
例 11.6 将上题改用指向结构体变量的指针作实参。
#include <stdio.h>
struct student
{
int num;
char name[20];
float score[3];
};stu={12345,″LiLi″,67.5,89,78.6};
void main()
{void print(struct student *);
/*形参类型修改成指向结构体的指针变量 */
print(&stu);} /*实参改为 stu的起始地址 */
void print(struct student *p) /*形参类型修改了 */
{ printf(FORMAT,p->num,p->name,
p->score[ 0],p->score[ 1],p->score[ 2]);
/*用指针变量调用各成员的值 */
printf( ″\n ″); }
运行结果

12345
Li Li
67.500000
89.000000
78.599998
11.6 指向结构体类型数据的指针程序分析:
此程序改用在定义结构体变量 stu时赋初值,这样程序可简化些。 print函数中的形参p被定义为指向 struct student类型数据的指针变量。注意在调用 print函数时,用结构体变量 str的起始地址& stu
作实参。在调用函数时将该地址传送给形参 p(p是指针变量)。这样p就指向 stu。在 print函数中输出
p所指向的结构体变量的各个成员值,它们也就是
stu的成员值。
main函数中的对各成员赋值也可以改用 scanf函数输入。 图 11-9
§ 11.7 用指针处理链表
11.7.1 链表概述链表是一种常见的重要的数据结构,是动态地进行存储分配的一种结构。
链表的组成:
头指针:存放一个地址,该地址指向一个元素
结点:用户需要的实际数据和链接节点的指针图 11-10
§ 11.7 用指针处理链表用结构体建立链表:
struct student
{ int num;
float score;
struct student *next ; };
其中成员 num和 score用来存放结点中的有用数据(用户需要用到的数据),next
是指针类型的成员,它指向 struct
student类型数据(这就是 next所在的结构体类型)
图 11-11
11.7 用指针处理链表
11.7.2 简单链表
#include <stdio.h>
#define NULL 0
struct student
{long num; float score; struct student *next; };
main()
{ struct student a,b,c,*head,*p;
a,num=99101; a.score=89.5;
b,num=99103; b.score=90;
c,num=99107; c.score=85;
head=&a; a.next=&b; b.next=&c;
c.next=NULL; p=head;
do {printf("%ld %5.1f\n",p->num,p->score);
p=p->next; } while(p!=NULL);
}
运行结果:
1010189.5
1010390.0
1010785.0
11.7 用指针处理链表程序分析:
开始时使 head指向 a结点,a.next指向 b结点,
b.next指向 c结点,这就构成链表关系。
,c.next=NULL” 的作用是使 c.next不指向任何有用的存储单元。在输出链表时要借助 p,先使 p指向 a
结点,然后输出 a结点中的数据,,p=p->next” 是为输出下一个结点作准备。 p->next的值是 b结点的地址,因此执行,p=p->next” 后 p就指向 b结点,所以在下一次循环时输出的是 b结点中的数据。
11.7 用指针处理链表
11.7.3处理动态链表所需的函数库函数提供动态地开辟和释放存储单元的有关函数:
(1) malloc函数其函数原型为 void *malloc(unsigned int size);其作用是在内存的动态存储区中分配一个长度为
size的连续空间。此函数的值(即“返回值”

是一个指向分配域起始地址的指针(类型为
void)。如果此函数未能成功地执行(例如内存空间不足),则返回空指针 (NULL)。
11.7 用指针处理链表
(2) calloc函数其函数原型为 void *calloc( unsigned n,
unsigned size) ;其作用是在内存的动态存储区中分配n个长度为 size的连续空间。函数返回一个指向分配域起始地址的指针;如果分配不成功,返回 NULL。
用 calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为
Size。
11.7 用指针处理链表
(3) free函数其函数原型为 void free( void *p) ;其作用是释放由p指向的内存区,使这部分内存区能被其他变量使用。p是最近一次调用 calloc或
malloc函数时返回的值。 free函数无返回值。
以前的C版本提供的 malloc和 calloc函数得到的是指向字符型数据的指针。 ANSI C提供的 malloc和 calloc函数规定为 void类型。
11.7 用指针处理链表
11.7.4 建立动态链表所谓建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系例 11.5 写一函数建立一个有 3名学生数据的单向动态链表。
算法如图图 11-12
11.7 用指针处理链表算法的实现:
我们约定学号不会为零,如果输入的学号为
0,则表示建立链表的过程完成,该结点不应连接到链表中。
如果输入的 p1->num不等于0,则输入的是第一个结点数据( n=1),令 head= p1,即把 p1的值赋给 head,也就是使 head也指向新开辟的结点 p1
所指向的新开辟的结点就成为链表中第一个结点图 11-13
11.7 用指针处理链表算法的实现:
再开辟另一个结点并使 p1指向它,接着输入该结点的数据,
如果输入的 p1->num≠ 0,则应链入第2个结点
( n=2),将新结点的地址赋给第一个结点的
next成员,
接着使p2=p1,也就是使p2指向刚才建立的结点图 11-14
11.7 用指针处理链表算法的实现:
再开辟一个结点并使 p1指向它,并输入该结点的数据。
在第三次循环中,由于n=3(n ≠ 1),又将p1的值赋给p2 ->next,也就是将第
3个结点连接到第2个结点之后,并使p2=
p1,使p2指向最后一个结点,
图 11-15
11.7 用指针处理链表算法的实现:
再开辟一个新结点,并使 p1指向它,输入该结点的数据。由于 p1->num的值为0,不再执行循环
,此新结点不应被连接到链表中,
将 NULL赋给 p2->next.
建立链表过程至此结束,p1最后所指的结点未链入链表中,第三个结点的 next成员的值为 NULL,它不指向任何结点。
图 11-16
11.7 用指针处理链表建立链表的函数如下,
#include <stdio.h>
#include <malloc.h>
#define NULL 0 //令 NULL代表0,用它表示,空地址
#define LEN sizeof(struct student) //令 LEN代表 struct
//student类型数据的长度
struct student
{ long num;
float score; struct student *next;
};int n; //n为全局变量,本文件模块中各函数均可使用它
11.7 用指针处理链表
struct student *creat()
{struct student *head; struct student *p1,*p2; n=0;
p1=p2=( struct student*) 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;
p2=p1; p1=(struct student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
}
p2->next=NULL; return(head);}
11.7 用指针处理链表
11.7.5 输出链表首先要知道链表第一个结点的地址,也就是要知道 head的值 。 然后设一个指针变量 p,先指向第一个结点,输出p所指的结点,然后使p后移一个结点,再输出,直到链表的尾结点 。
图 11-17,11-18
11.7 用指针处理链表例1 1,9 编写一个输出链表的函数 print.
void print(struct student *head)
{struct student *p;
printf("\nNow,These %d records are:\n",n);
p=head;
if(head!=NULL)
do
{printf("%ld %5.1f\n",p->num,p->score);
p=p->next;
}while(p!=NULL);
}
11.7 用指针处理链表
11.7.6对链表的删除操作从一个动态链表中删去一个结点,并不是真正从内存中把它抹掉,而是把它从链表中分离开来,只要撤销原来的链接关系即可。
图 11-19
11.7 用指针处理链表例 11.10写一函数以删除动态链表中指定的结点,
解题思路,
从 p指向的第一个结点开始,检查该结点中的
num值是否等于输入的要求删除的那个学号 。 如果相等就将该结点删除,如不相等,就将 p后移一个结点,再如此进行下去,直到遇到表尾为止 。
11.7 用指针处理链表可以设两个指针变量 p1和 p2,先使 p1指向第一个结点 。
如果要删除的不是第一个结点,则使 p1后移指向下一个结点 (将 p1->next赋给 p1),在此之前应将 p1的值赋给 p2,使 p2指向刚才检查过的那个结点 。
11.7 用指针处理链表注意:
①要删的是第一个结点(p1的值等于hea
d的值,如图1 1-2 0(a)那样),则应将p1 ->
next赋给head。这时head指向原来的第二个结点。第一个结点虽然仍存在,但它已与链表脱离,因为链表中没有一个结点或头指针指向它
。虽然p1还指向它,它仍指向第二个结点,但仍无济于事,现在链表的第一个结点是原来的第二个结点,原来第一个结点已“丢失”,即不再是链表中的一部分了。
11.7 用指针处理链表注意:
②如果要删除的不是第一个结点,则将p1 ->
next赋给p2 ->next,见图1 1 2 0(d
)。p2 ->next原来指向p1指向的结点(图中第二个结点),现在p2 ->next改为指向p
1 ->next所指向的结点(图中第三个结点)。
p1所指向的结点不再是链表的一部分。
还需要考虑链表是空表(无结点)和链表中找不到要删除的结点的情况。
11.7 用指针处理链表图 11-20
11.7 用指针处理链表算法:
图 11-21
11.7 用指针处理链表删除结点的函数 del:
struct student *del(struct student *head,long num)
{struct student *p1,*p2;
if (head==NULL){printf("\nlist null!\n");goto end;}
p1=head;
while(num!=p1->num && p1->next!=NULL)
{p2=p1;p1=p1->next;}
if(num==p1->num)
{if(p1==head) head=p1->next;
else p2->next=p1->next;
printf("delete:%ld\n",num); n=n-1; }
else printf("%ld not been found!\n",num);
end;return(head);}
11.7 用指针处理链表
11.7.7对链表的插入操作对链表的插入是指将一个结点插入到一个已有的链表中。
为了能做到正确插入,必须解决两个问题:
① 怎样找到插入的位置;
② 怎样实现插入。
11.7 用指针处理链表先用指针变量 p0指向待插入的结点,p1指向第一个结点。
将 p0->num与 p1->num相比较,如果 p0->num>
p1-> num,则待插入的结点不应插在 p1所指的结点之前。此时将 p1后移,并使 p2指向刚才 p1
所指的结点。
11.7 用指针处理链表再将 p1->num与 p0->num比,如果仍然是 p0->num
大,则应使 p1继续后移,直到 p0->p1-> num为止。
这时将 p0所指的结点插到 p1所指结点之前。但是如果 p1所指的已是表尾结点,则 p1就不应后移了。如果 p0-> num比所有结点的 num都大,则应将 p0所指的结点插到链表末尾。
如果插入的位置既不在第一个结点之前,又不在表尾结点之后,则将 p0的值赋给 p2->next,使
p2->next指向待插入的结点,然后将 p1的值赋给
p0->next,使得 p0->next指向 p1指向的变量。
§ 11.7 用指针处理链表如果插入位置为第一个结点之前 (即 p1等于
head时 ),则将 p0赋给 head,将 p1赋给 p0->next
如果要插到表尾之后,应将 p0赋给 p1->next,
NULL赋给 p0->next
图 11-22
11.7 用指针处理链表算法:
图 11-23
11.7 用指针处理链表例 11.11插入结点的函数 insert如下 。
struct student *insert(struct student *head,struct student *stud)
{struct student *p0,*p1,*p2;
p1=head;p0=stud; if(head==NULL)
{head=p0; p0->next=NULL;}
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;}
else {p1->next=p0; p0->next=NULL;}}
n=n+1; return(head);
}
11.7 用指针处理链表
11.7.8 对链表的综合操作将以上建立,输出,删除,插入的函数组织在一个 C程序中,用main函数作主调函数 。
void main()
{
struct student *head,stu;long del_num;
prinf(″intput records:\n″) ;
head=creat();print(head);printf (″ \n intput the
deleted number:\n″);
scanf (″%ld″,&del_num) ;head=del(head,del_num);
print(head);
printf (″ \n intput the deleted number:\n″);
scanf (″%ld″,&stu.num,&stu.score) ;
head=insert(head,&stu);
print(head);
}
11.7 用指针处理链表此程序运行结果是正确的 。 它只删除一个结点,插入一个结点 。 但如果想再插入一个结点,
重复写上程序最后 4行,共插入两个结点,运行结果却是错误的 。
Input records,(建立链表)
10101,90 ↙
10103,98 ↙
10105,76 ↙
0,0 ↙
11.7 用指针处理链表
Now,these 3 records are:
1010190.0
1010398.0
1010576.0
intput the deleted number,10103 (删除)
delete,10103 ↙
Now,these 4 records are:
1010190.0
1010576.0
11.7 用指针处理链表
input the inserted record
(插入第一个结点)
10102,90↙
Now,these 3 records are:
1010190.0
1010290.0
1010576.0
input the inserted record
(插入第二个结点)
10104,99↙
Now,these 4 records are:
1010190.0
1010499.0
1010499.0
1010499.0
11.7 用指针处理链表出现以上结果的原因是:
stu是一个有固定地址的结构体变量。第一次把 stu结点插入到链表中,第二次若再用它来插入第二个结点,就把第一次结点的数据冲掉了,实际上并没有开辟两个结点。为了解决这个问题,必须在每插入一个结点时新开辟一个内存区。我们修改 main函数,使之能删除多个结点(直到输入要删的学号为 0
),能插入多个结点(直到输入要插入的学号为 0)。
11.7 用指针处理链表
main()
{struct student *head,*stu;
long del_num;printf("input records:\n");
head=creat(); print (head);
printf("\ninput the deleted number:");
scanf("%ld",&del_num);
while (del_num!=0){head=del(head,del_num);
print (head);printf ("input the deleted number:");
scanf("%ld",&del_num);} printf("\ninput the inserted
record:");stu=(struct student *) malloc(LEN);
scanf("%ld,%f",&stu->num,&stu->score);
while(stu->num!=0){head=insert(head,stu); printf("input the
inserted record:");stu=(struct student *)malloc(LEN);
scanf("%ld,%f",&stu->num,&stu->score);
}}
11.7 用指针处理链表
stu定义为指针变量,在需要插入时先用
malloc函数开辟一个内存区,将其起始地址经强制类型转换后赋给 stu,然后输入此结构体变量中各成员的值。对不同的插入对象,stu的值是不同的,每次指向一个新的 struct student变量。在调用 insert函数时,实参为 head和 stu,将已建立的链表起始地址传给 insert函数的形参,将 stu(
即新开辟的单元的地址)传给形参 stud,返回的函数值是经过插入之后的链表的头指针(地址)
11.7 用指针处理链表运行结果:
input records:
10
10
10
Now,These 3 records are:
10101 99.0
10103 87.0
10105 77.0
11.7 用指针处理链表
intput the deleted number
10103 (删除)
delete,10103 ↙
Now,these 4 records are
10101 9 9.0
10105 76.0
intput the deleted number
10103 (删除)
delete,1010 5↙
Now,these 4 records are
10101 9 9.0
11.7 用指针处理链表
intput the deleted
number:0
input the inserted
record
10104,87↙
Now,these 3 records are
10101 99.0
10104 87
input the inserted
record
10106,65↙
Now,these 3 records are
10101 99.0
10104 87
10106 65.0
11.8 共用体
11.8.1共用体的概念使几个不同的变量共占同一段内存的结构 称为
,共用体,类型的结构。
定义共用体类型变量的一般形式为:
union 共用体名

成员表列
} 变量表列;
图 11-24
11.8 共用体例如:
union data union data
{ int i; { int i;
char ch; 或 char ch;
float f; float f;
} a,b,c; };union data a,b,c;
11.8 共用体共用体和结构体的比较:
结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。
共用体变量所占的内存长度等于最长的成员的长度。
共用体和结构体的比较:
结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。
共用体变量所占的内存长度等于最长的成员的长度。
例如,上面定义的,共用体,变量a、b、c各占

个字节(因为一个实型变量占4个字节),而不是各占2+1+4=7个字节。
11.8 共用体
11.8.2 共用体变量的引用方式只有先定义了共用体变量才能引用它,而且不能引用共用体变量,而只能引用共用体变量中的成员。
例如,前面定义了 a,b,c为共用体变量
a.i (引用共用体变量中的整型变量i)
a.ch(引用共用体变量中的字符变量ch)
a.f (引用共用体变量中的实型变量f)
11.8 共用体
11.8.3 共用体类型数据的特点
(1)同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种。
(2) 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。
(3) 共用体变量的地址和它的各成员的地址都是同一地址。
11.8 共用体
(4) 不能对共用体变量名赋值,也不能企图引用变量名来得到一个值,又不能在定义共用体变量时对它初始化。
(5) 不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针
(6) 共用体类型可以出现在结构体类型定义中,
也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。
11.8 共用体例1 1,12 设有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码
、性别、职业,班级 。教师的数据包括:
姓名、号码、性别、职业,职务 。可以看出,学生和教师所包含的数据是不同的。
现要求把它们放在同一表格中。
图 11-25
11.7 用指针处理链表算法:
图 11-26
11.8 共用体
#include <stdio.h>
struct
{
int num;
char name[10];
char sex;
char job;
union
{
int banji;
char position[10];
}category;
}person[2];/*先设人数为 2*/
11.8 共用体
void main()
{int i;
for(i=0;i<2;i++)
{scanf("%d %s %c %c",&person[i].num,&person[i].name,
&person[i].sex,&person[i].job);
if(person[i].job == 'S')
scanf("%d",&person[i].category.banji);
else if(person[i].job == 'T')
scanf("%s",person[i].category.position);
else printf(,Input error!” );} printf("\n");
printf("No,name sex job class/position\n");
for(i=0;i<2;i++)
{if (person[i].job == 'S')
printf(,%-6d%-10s%-3c%-3c%-6d\n”,person[i].num,
person[i].name,person[i].sex,person[i].job,
person[i].category.banji);
else printf(,%-6d%-10s%-3c%-3c%-6s\n”,person[i].num,
person[i].name,person[i].sex,person[i].job,
person[i].category.position);}}
运行情况如下:
101 Li f s
Wang m t
No,Name sex jobclass/position
101 Li f s 501
102 Wang m t professor
11.9 枚举类型枚举,将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
申明枚举类型用 enum
enum weekday{sun,mon,tue,wed,thu,fri,sat};
定义变量:
enum weekday workday,week-day;
enum{sun,mon,tue,wed,thu,fri,sat} workday;
变量值只能是 sun到 sat之一 。
枚举元素枚举常量
11.9 枚举类型说明:
(1)在C编译中,对枚举元素按常量处理,故称枚举常量。它们不是变量,不能对它们赋值。
(2) 枚举元素作为常量,它们是有值的,C语言编译按定义时的顺序使它们的值为0,1,2 …
(3) 枚举值可以用来作判断比较。
(4) 一个整数不能直接赋给一个枚举变量。
11.9 枚举类型例1 1.1 3口袋中有红、黄、蓝、白、黑 5种颜色的球若干个。每次从口袋中先后取出3个球,问得到 3种不同色的球的可能取法,输出每种排列的情况。
算法:
图 11-27 11-28
§ 13.9 枚举类型
#include <stdio.h>
main()
{enum color {red,yellow,blue,white,black};
enum color i,j,k,pri; int n,loop;n=0;
for (i=red;i<=black;i++)
for (j=red;j<=black;j++)
if (i!=j)
{ for (k=red;k<=black;k++)
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;
}
§ 13.9 枚举类型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);
}
运行情况如下:
1redyellowblue2redyellowwhite3redyellowblack
58blackwhitered59blackwhiteyellow60blackwhiteblue
total:60
11.10 用 typedef定义类型用 typedef声明新的类型名来代替已有的类型名。
声明 INTEGER为整型
typedef int INTEGER
声明结构类型
Typedef struct{
int month;
int day;
int year; }DATE;
11.10 用 typedef定义类型声明NUM为整型数组类型,
typedef int NUM[100];
声明STRING为字符指针类型:
typedef char *STRING;
声明 POINTER为指向函数的指针类型,该函数返回整型值,
typedef int (*POINTER)()
11.10 用 typedef定义类型用 typedef定义类型的方法:
① 先按定义变量的方法写出定义体(如,int i)。
② 将变量名换成新类型名(例如:将 i换成 COUNT)。
③ 在最前面加typedef
(例如,typedef int COUNT)。
④ 然后可以用新类型名去定义变量。
11.10 用 typedef定义类型用 typedef定义类型的方法(举例):
① 先按定义数组变量形式书写,int n[100];
② 将变量名n换成自己指定的类型名:
int NUM[10 0];
③ 在前面加上 typedef,得到
typedef int NUM[100];
④ 用来定义变量,NUM n;
11.10 用 typedef定义类型说明:
(1)用 typedef可以声明各种类型名,但不能用来定义变量。
(2) 用 typedef只是对已经存在的类型增加一个类型名,
而没有创造新的类型。
(3) 当不同源文件中用到同一类型数据时,常用
typedef声明一些数据类型,把它们单独放在一个文件中,然后在需要用到它们的文件中用 #include命令把它们包含进来。
(4) 使用 typedef有利于程序的通用与移植。
11.10 用 typedef定义类型说明:
(5) typedef与 #define有相似之处,例如:
typedef int COUNT; #define COUNT int的作用都是用 COUNT代表 int。但事实上,它们二者是不同的。
#define是在预编译时处理的,它只能作简单的字符串替换,而 typedef是在编译时处理的。实际上它并不是作简单的字符串替换,而是采用如同定义变量的方法那样来声明一个类型。