第九章
本章要点结构体的概念结构体的定义和引用结构体数组
主要内容
9.1 结构体类型和结构体变量
9.2 结构体数组
9.3 结构体指针
9.4 用指针处理链表
9.5 共用体类型
9.6 枚举类型
9.7 用 typedef命名类型
§ 9.1 结构体类型和结构体变量
9.1.1 结构体类型
问题定义:
有时需要将不同类型的数据组合成一个有机的整体,以便于引用。 如:
一个学生有学号 /姓名 /性别 /年龄 /地址等属性
int num; char name[20]; char sex;
int age; int char addr[30];应当把它们组织成一个组合项,在一个组合项中包含若干个类型不同(当然也可以相同)
的数据项。
图 9-1
100101 Li Fun M 18 87.5 Beijing
Num name sex age score addr
声明一个结构体类型的一般形式为:
struct 结构体名
{成员表列};
如,struct student
{
int num;char name[20];char sex;
int age;float score;char addr[30];
}
结构体名类型名 成员名
§ 9.1 结构体类型和结构体变量
9.1.1 结构体类型说明:
(1)结构体类型并不是只有一种,而是可以设计出许多种结构体类型,
(2) 成员也可以是一个结构体变量。
(3),结构体”这个词是根据英文单词struct
ure译出的。
§ 9.1 结构体类型和结构体变量
9.1.1 结构体类型
9.1.2 定义结构体类型变量
可以采取以下 3种方法定义结构体类型变量:
(1)先声明结构体类型再定义变量名例如,struct student student1,student2;
| | |
结构体类型名 结构体变量名定义了 student1和 student2为 struct student
类型的变量,即它们具有 struct student
类型的结构,图 9-3
student1
100101 ZhangXin M 19 90.5 Shanghai
100102 WangLi F 20 98 Beijing
student2
在定义了结构体变量后,系统会为之分配内存单元。
例如,student1和 student2在 Turbo C的内存中各占 59个字节( 2+20+1+2+4+30=59)。
(2)在声明类型的同时定义变量这种形式的定义的一般形式为,
struct 结构体名
{
成员表列
} 变量名表列;
9.1.2 定义结构体类型变量例如:
struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
} student1,student2;
它的作用与第一种方法相同,即定义了两个 struct
student 类型的变量 student1,
student2
9.1.2 定义结构体类型变量
(3) 不指定类型名而直接定义结构体类型变量其一般形式为,
struct
{
成员表列
} 变量名表列;
即不出现结构体名。
注意:
(1) 结构体类型与结构体变量
(2) 结构体类型中的成员名可以与程序中的变量名相同,但二
(3) 对结构体变量中的成员(
即“域”),可以单独使用,
它的作用与地位相当于普通变量。
9.1.2 定义结构体类型变量
9.1.3 引用结构体变量
在定义了结构体变量以后,当然可以引用这个变量 。 但应遵守以下规则,
(1)同类的结构体变量可以互相赋值,如:
student1=student2;
不能将一个结构体变量作为一个整体进行输入和输出。
例如,已定义 student1和 student2为结构体变量并且它们已有值 。
printf(″% d,%s,%c,%d,%f,%\ n″,student1);?
引用结构体变量中成员的方式为结构体变量名,成员名例如,student1.num表示 student1变量中的
num成员,即 student1的 num(学号 )项 。 可以 对 变 量 的 成 员 赋 值,例如
:student1.num=10010;“.”是成员 (分量 )运算符,它在所有的运算符中优先级最高,因此可以把 student1.num作为一个整体来看待 。 上面赋值语句的作用是将整数 10010
赋给 student1变量中的成员 num。
9.1.3 引用结构体变量
(2) 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。
例如,对上面定义的结构体变量 student1,可以这样访问各成员,
student1.num
student1.birthday.month
注意:
不能用
student1.birthday
来访问 student1变量中的成员
birthday,因为
birthday本身是一个结构体变量。
9.1.3 引用结构体变量
(3) 对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
例如:
student2.score=student1.score;
sum=student1.score+student2.score;
student1.age++;
++student2.age;
由于“.”运算符的优先级最高,因此s
tudent1.a
ge++是对stu
dent1.age
进行自加运算,而不是先对age进行自加运算。
9.1.3 引用结构体变量
(4) 可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
例如:
scanf(″%d″,&student1.num);
(输入 student1.num的值)
printf(″%o″,& student1);
(输出 student1的首地址)
9.1.3 引用结构体变量但不能用以下语句整体读入结构体变量,
例如:
scanf( ″%d,% s,% c,% d,% f,%
s″,& student1);
结构体变量的地址主要用作函数参数,
传递结构体变量的地址。
9.1.3 引用结构体变量
9.1.4 结构体变量的初始化但不能用以下语句整体读入结构体变量,
例如:
scanf( ″%d,% s,% c,% d,% f,%
s″,& student1);
结构体变量的地址主要用作函数参数,
传递结构体变量的地址。
例 9.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
§ 9.2 结构体数组一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。结构体数组与以前介绍过的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。
§ 9.2 结构体数组
9.2.1 定义结构体数组和定义结构体变量的方法相仿,只需说明其为数组即可。 例如:
struct student
{int num;char name[20];char sex;int age;
float score;char addr[30];
};struct student[3];
以上定义了一个数组 stu,数组有3个元素,均为 struct
student类型数据。
§ 9.2 结构体数组也可以直接定义一个结构体数组,例如,
struct student
{int num;
… };stu[3];
或:
strcut student
{int num;
… };stu[3];图 9-4
§ 9.2 结构体数组
9.2.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″}} ; 图 9-5
§ 9.2 结构体数组当然,数组的初始化也可以用以下形式:
struct student
{ int num;
…
};
struct student str[]{{… },{… },{… }};
即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。
结构体数组初始化的一般形式是在定义数组的后面加上
“={初值表列};”。
§ 9.2 结构体数组
9.2.3 结构体数组应用举例例 9.2对候选人得票的统计程序。设有 3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。
#include <string.h>
#include <stdio.h>
struct person
{
char name[20];in count;
};
leader[3]={“Li”,0,”Zhang”,0,”Fun”,0}
例 9.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
§ 9.2 结构体数组程序定义一个全局的结构体数组 leader,它有3个元素,每一个元素包含两个成员
name(姓名)和 count(票数)。在定义数组时使之初始化,使 3位候选人的票数都先置零,
在主函数中定义字符数组 leader-name,它代表被选人的姓名,在 10次循环中每次先输入一个被选人的具体人名,然后把它与 3个候选人姓名相比,看它和哪一个候选人的名字相同。在输入和统计结束之后,将 3人的名字和得票数输出。 图 9-6
Li 0
Zhang 0
Fun 0
name count
§ 9.3 结构体指针结构体指针是指向结构体数据的指针,一个结构体变量的起始地址就是这个结构体变量的指针。指针变量既可以指向结构体变量,也可以用来指向结构体数组中的元素。但是,指针变量的基类型必须与结构体变量的类型相同。
例如:
struct student pt; /* pt可以指 向
struct student类型的数据 */
9.3.1 指向结构体变量的指针变量下面通过一个简单例子来说明指向结构体变量的指针变量的应用。
例 9.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
9.3.1 指向结构体变量的指针变量程序分析:
在函数的执行部分将结构体变量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
这样的形式。
图 9-7
9.3.1 指向结构体变量的指针变量以下 3种形式等价:
① 结构体变量.成员名
②( *p).成员名
③ p ->成员名其中 ->称为指向运算符。请分析以下几种运算:
p ->n得到p指向的结构体变量中的成员n的值。
p ->n++ 得到p指向的结构体变量中的成员n
的值,用完该值后使它加1。
++p ->n 得到p指向的结构体变量中的成员n
的值加1,然后再使用它。
9.3.2 指向结构体数组的指针
11.6.2 指向结构体数组的指针例 9.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
9.3.2 指向结构体数组的指针程序分析:
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了,不再执行循环。 图 9-8
9.3.2 指向结构体数组的指针注意:
(1) 如果p的初值为 stu,即指向第一个元素,
则p加1后 p就指向下一个元素。 例如,
(++p)->num 先使p自加1,然后得到它指向的元素中的 num成员值(即 10102)。
(p++)->num 先得到p ->num的值(即 10101)
,然后使p自加1,指向 stu[1]。
请注意以上二者的不同。
9.3.2 指向结构体数组的指针注意:
(2) 程序已定义了p是一个指向 struct student
类型数据的指针变量,它用来指向一个 struct
student类型的数据,不应用来指向 stu数组元素中的某一成员。
例如,p=stu[ 1].n ame ;
如果要将某一成员的地址赋给 p,可以用强制类型转换,先将成员的地址转换成 p的类型。
例如:p=(struct student *)
stu[ 0].n ame;
§ 9.3 结构体指针
9.3.3 结构体变量和指向结构体的指针作函数参数将一个结构体变量的值传递给另一个函数,有 3个方法,
(1) 用结构体变量的成员作参数 。
(2) 用结构体变量作实参 。
(3) 用指向结构体变量 ( 或数组 ) 的指针作实参,将结构体变量 ( 或数组 ) 的地址传给形参,
§ 9.3 结构体指针
11.6.2 指向结构体数组的指针例 9.5 有一个结构体变量 stu,内含学生学号、姓名和 3
门课程的成绩。要求在 main函数中赋予值,在另一函数
print中将它们输出。今用结构体变量作函数参数。
#include <stdio.h>
struct student
{
int num;
char name[20];
float score[3];
};
§ 9.3 结构体指针
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
例 9.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
§ 9.3 结构体指针程序分析:
此程序改用在定义结构体变量 stu时赋初值,这样程序可简化些。 print函数中的形参p被定义为指向 struct student类型数据的指针变量。注意在调用 print函数时,用结构体变量 str的起始地址& stu
作实参。在调用函数时将该地址传送给形参 p(p是指针变量)。这样p就指向 stu。在 print函数中输出
p所指向的结构体变量的各个成员值,它们也就是
stu的成员值,
main函数中的对各成员赋值也可以改用 scanf函数输入,图 9-9
§ 9.4 用指针处理链表
9.4.1 链表概述链表是一种常见的重要的数据结构,是动态地进行存储分配的一种结构。
链表的组成:
头指针:存放一个地址,该地址指向一个元素
结点:用户需要的实际数据和链接节点的指针图 9-10
§ 9.4 用指针处理链表用结构体建立链表:
struct student
{ int num;
float score;
struct student *next ; };
其中成员 num和 score用来存放结点中的有用数据(用户需要用到的数据),next
是指针类型的成员,它指向 struct
student类型数据(这就是 next所在的结构体类型)
图 9-11
§ 9.4 用指针处理链表
9.4.2 建立简单的静态链表例 9.7 建立一个如图 9.11所示的简单链表,它由 3个学生数据的结点组成。输出各结点中的数据。
#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
§ 9.4 用指针处理链表程序分析:
开始时使 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结点中的数据。
§ 9.4 用指针处理链表
9.4.3 建立动态链表所谓建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系例 9.8 写一函数建立一个有 3名学生数据的单向动态链表,
算法如图图 9-12
§ 9.4 用指针处理链表算法的实现:
我们约定学号不会为零,如果输入的学号为
0,则表示建立链表的过程完成,该结点不应连接到链表中。
如果输入的 p1->num不等于0,则输入的是第一个结点数据( n=1),令 head= p1,即把 p1的值赋给 head,也就是使 head也指向新开辟的结点 p1
所指向的新开辟的结点就成为链表中第一个结点图 9-13
§ 9.4 用指针处理链表算法的实现:
再开辟另一个结点并使 p1指向它,接着输入该结点的数据,
如果输入的 p1->num≠ 0,则应链入第2个结点
( n=2),将新结点的地址赋给第一个结点的
next成员,
接着使p2=p1,也就是使p2指向刚才建立的结点图 9-14
§ 9.4 用指针处理链表算法的实现:
再开辟一个结点并使 p1指向它,并输入该结点的数据,
在第三次循环中,由于n=3(n ≠ 1),又将p1的值赋给p2 ->next,也就是将第
3个结点连接到第2个结点之后,并使p2=
p1,使p2指向最后一个结点,
图 9-15
§ 9.4 用指针处理链表算法的实现:
再开辟一个新结点,并使 p1指向它,输入该结点的数据。由于 p1->num的值为0,不再执行循环
,此新结点不应被连接到链表中,
将 NULL赋给 p2->next.
建立链表过程至此结束,p1最后所指的结点未链入链表中,第三个结点的 next成员的值为 NULL,它不指向任何结点。
图 9-16
§ 9.4 用指针处理链表建立链表的函数如下,
#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为全局变量,本文件模块中各函数均可使用它
§ 9.4 用指针处理链表
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);
}
§ 9.4 用指针处理链表
9.4.4 输出链表首先要知道链表第一个结点的地址,也就是要知道 head的值 。 然后设一个指针变量 p,先指向第一个结点,输出p所指的结点,然后使p后移一个结点,再输出,直到链表的尾结点 。
图 9-17,9-18
§ 9.4 用指针处理链表例 9,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);
}
§ 9.4 用指针处理链表可以把例 9.7和例 9.9合起来加上一个主函数,组成一个程序,即,
#include <stdio.h>
#include <malloc.h>
#define LEN sizeof(struct student)
struct student
{long num;
float score; struct student *next;
};
int n;
§ 9.4 用指针处理链表
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;
§ 9.4 用指针处理链表
p2=p1;
p1=(struct student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
}
p2->next=NULL;
return(head);
}
void print(struct student head) /* 输出链表的函数 */
{struct student *p;
printf("\nNow,These %d records are:\n",n);
§ 9.4 用指针处理链表
p=head;
if(head!=NULL)
do
{printf("%ld %5.1f\n",p->num,p->score);
p=p->next;
}while(p!=NULL);
}
void main()
{struct student *head ;
head=creat();
print(head); /* 调用 print函数 */
}
§ 9.5 共用体类型
9.5.1 什么是共用体类型使几个不同的变量共占同一段内存的结构 称为
,共用体,类型的结构,
定义共用体类型变量的一般形式为:
union 共用体名
{
成员表列
} 变量表列;
图 9-19
§ 9.5 共用体类型例如:
union data union data
{ int i; { int i;
char ch; 或 char ch;
float f; float f;
} a,b,c; };union data a,b,c;
§ 9.5 共用体类型共用体和结构体的比较:
结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。
共用体变量所占的内存长度等于最长的成员的长度。
共用体和结构体的比较:
结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。
共用体变量所占的内存长度等于最长的成员的长度。
例如,上面定义的,共用体,变量a、b、c各占
4
个字节(因为一个实型变量占4个字节),而不是各占2+1+4=7个字节。
§ 9.5 共用体类型
9.5.2 共用体变量的引用方式只有先定义了共用体变量才能引用它,而且不能引用共用体变量,而只能引用共用体变量中的成员。
例如,前面定义了 a,b,c为共用体变量
a.i (引用共用体变量中的整型变量i)
a.ch(引用共用体变量中的字符变量ch)
a.f (引用共用体变量中的实型变量f)
§ 9.5 共用体类型
9.5.3 共用体类型数据的特点
(1)同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种。
(2) 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。
(3) 共用体变量的地址和它的各成员的地址都是同一地址。
§ 9.5 共用体类型
(4) 不能对共用体变量名赋值,也不能企图引用变量名来得到一个值,又不能在定义共用体变量时对它初始化。
(5) 以前的 C规定不能把共用体变量作为函数参数
,但可以使用指向共用体变量的指针作函数参数。 ANSI新标准放宽了限制,允许用共用体变量作为函数参数。
(6) 共用体类型可以出现在结构体类型定义中,
也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。
§ 9.5 共用体类型例 9.10 设有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、
性别、职业,班级 。教师的数据包括:姓名、号码、性别、职业,职务 。可以看出
,学生和教师所包含的数据是不同的。现要求把它们放在同一表格中。
图 9-10
§ 9.5 共用体类型
#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
9.5 共用体类型也可以不在结构体类型的声明中声明共用体类型,
而把它放在结构体类型的声明之前,即:
{int i;
union categ { int banji;
char position[10];
};
struct { int num;
char name[10];
char sex;
char job;
union categ category;}person[2];
§ 9.6 枚举类型枚举,将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
申明枚举类型用 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之一枚举元素枚举常量
§ 9.6 枚举类型说明:
(1)在C编译中,对枚举元素按常量处理,故称枚举常量。它们不是变量,不能对它们赋值。
(2) 枚举元素作为常量,它们是有值的,C语言编译按定义时的顺序使它们的值为0,1,2 …
(3) 枚举值可以用来作判断比较。
(4) 一个整数不能直接赋给一个枚举变量。
§ 9.6 枚举类型例1 1.1 3口袋中有红、黄、蓝、白、黑 5种颜色的球若干个。每次从口袋中先后取出3个球,问得到 3种不同色的球的可能取法,输出每种排列的情况。
算法:
图 9-22 9-23
§ 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
§ 9.7 用 typedef命名类型用 typedef声明新的类型名来代替已有的类型名声明 INTEGER为整型
typedef int INTEGER
声明结构类型
Typedef struct{
int month;
int day;
int year; }DATE;
§ 9.7 用 typedef命名类型声明NUM为整型数组类型
typedef int NUM[100];
声明STRING为字符指针类型
typedef char *STRING;
声明 POINTER为指向函数的指针类型,该函数返回整型值
typedef int (*POINTER)()
§ 9.7 用 typedef命名类型用 typedef定义类型的方法
① 先按定义变量的方法写出定义体(如,int i)。
② 将变量名换成新类型名(例如:将 i换成 COUNT)。
③ 在最前面加typedef
(例如,typedef int COUNT)。
④ 然后可以用新类型名去定义变量。
§ 9.7 用 typedef命名类型用 typedef定义类型的方法(举例)
① 先按定义数组变量形式书写,int n[100];
② 将变量名n换成自己指定的类型名:
int NUM[10 0];
③ 在前面加上 typedef,得到
typedef int NUM[100];
④ 用来定义变量,NUM n;
§ 9.7 用 typedef命名类型说明:
(1)用 typedef可以声明各种类型名,但不能用来定义变量。
(2) 用 typedef只是对已经存在的类型增加一个类型名,
而没有创造新的类型。
(3) 当不同源文件中用到同一类型数据时,常用
typedef声明一些数据类型,把它们单独放在一个文件中,然后在需要用到它们的文件中用 #include命令把它们包含进来。
(4) 使用 typedef有利于程序的通用与移植。
§ 9.7 用 typedef命名类型说明:
(5) typedef与 #define有相似之处,例如:
typedef int COUNT; #define COUNT int的作用都是用 COUNT代表 int。但事实上,它们二者是不同的。
#define是在预编译时处理的,它只能作简单的字符串替换,而 typedef是在编译时处理的。实际上它并不是作简单的字符串替换,而是采用如同定义变量的方法那样来声明一个类型
本章要点结构体的概念结构体的定义和引用结构体数组
主要内容
9.1 结构体类型和结构体变量
9.2 结构体数组
9.3 结构体指针
9.4 用指针处理链表
9.5 共用体类型
9.6 枚举类型
9.7 用 typedef命名类型
§ 9.1 结构体类型和结构体变量
9.1.1 结构体类型
问题定义:
有时需要将不同类型的数据组合成一个有机的整体,以便于引用。 如:
一个学生有学号 /姓名 /性别 /年龄 /地址等属性
int num; char name[20]; char sex;
int age; int char addr[30];应当把它们组织成一个组合项,在一个组合项中包含若干个类型不同(当然也可以相同)
的数据项。
图 9-1
100101 Li Fun M 18 87.5 Beijing
Num name sex age score addr
声明一个结构体类型的一般形式为:
struct 结构体名
{成员表列};
如,struct student
{
int num;char name[20];char sex;
int age;float score;char addr[30];
}
结构体名类型名 成员名
§ 9.1 结构体类型和结构体变量
9.1.1 结构体类型说明:
(1)结构体类型并不是只有一种,而是可以设计出许多种结构体类型,
(2) 成员也可以是一个结构体变量。
(3),结构体”这个词是根据英文单词struct
ure译出的。
§ 9.1 结构体类型和结构体变量
9.1.1 结构体类型
9.1.2 定义结构体类型变量
可以采取以下 3种方法定义结构体类型变量:
(1)先声明结构体类型再定义变量名例如,struct student student1,student2;
| | |
结构体类型名 结构体变量名定义了 student1和 student2为 struct student
类型的变量,即它们具有 struct student
类型的结构,图 9-3
student1
100101 ZhangXin M 19 90.5 Shanghai
100102 WangLi F 20 98 Beijing
student2
在定义了结构体变量后,系统会为之分配内存单元。
例如,student1和 student2在 Turbo C的内存中各占 59个字节( 2+20+1+2+4+30=59)。
(2)在声明类型的同时定义变量这种形式的定义的一般形式为,
struct 结构体名
{
成员表列
} 变量名表列;
9.1.2 定义结构体类型变量例如:
struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
} student1,student2;
它的作用与第一种方法相同,即定义了两个 struct
student 类型的变量 student1,
student2
9.1.2 定义结构体类型变量
(3) 不指定类型名而直接定义结构体类型变量其一般形式为,
struct
{
成员表列
} 变量名表列;
即不出现结构体名。
注意:
(1) 结构体类型与结构体变量
(2) 结构体类型中的成员名可以与程序中的变量名相同,但二
(3) 对结构体变量中的成员(
即“域”),可以单独使用,
它的作用与地位相当于普通变量。
9.1.2 定义结构体类型变量
9.1.3 引用结构体变量
在定义了结构体变量以后,当然可以引用这个变量 。 但应遵守以下规则,
(1)同类的结构体变量可以互相赋值,如:
student1=student2;
不能将一个结构体变量作为一个整体进行输入和输出。
例如,已定义 student1和 student2为结构体变量并且它们已有值 。
printf(″% d,%s,%c,%d,%f,%\ n″,student1);?
引用结构体变量中成员的方式为结构体变量名,成员名例如,student1.num表示 student1变量中的
num成员,即 student1的 num(学号 )项 。 可以 对 变 量 的 成 员 赋 值,例如
:student1.num=10010;“.”是成员 (分量 )运算符,它在所有的运算符中优先级最高,因此可以把 student1.num作为一个整体来看待 。 上面赋值语句的作用是将整数 10010
赋给 student1变量中的成员 num。
9.1.3 引用结构体变量
(2) 如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。
例如,对上面定义的结构体变量 student1,可以这样访问各成员,
student1.num
student1.birthday.month
注意:
不能用
student1.birthday
来访问 student1变量中的成员
birthday,因为
birthday本身是一个结构体变量。
9.1.3 引用结构体变量
(3) 对结构体变量的成员可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。
例如:
student2.score=student1.score;
sum=student1.score+student2.score;
student1.age++;
++student2.age;
由于“.”运算符的优先级最高,因此s
tudent1.a
ge++是对stu
dent1.age
进行自加运算,而不是先对age进行自加运算。
9.1.3 引用结构体变量
(4) 可以引用结构体变量成员的地址,也可以引用结构体变量的地址。
例如:
scanf(″%d″,&student1.num);
(输入 student1.num的值)
printf(″%o″,& student1);
(输出 student1的首地址)
9.1.3 引用结构体变量但不能用以下语句整体读入结构体变量,
例如:
scanf( ″%d,% s,% c,% d,% f,%
s″,& student1);
结构体变量的地址主要用作函数参数,
传递结构体变量的地址。
9.1.3 引用结构体变量
9.1.4 结构体变量的初始化但不能用以下语句整体读入结构体变量,
例如:
scanf( ″%d,% s,% c,% d,% f,%
s″,& student1);
结构体变量的地址主要用作函数参数,
传递结构体变量的地址。
例 9.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
§ 9.2 结构体数组一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。结构体数组与以前介绍过的数值型数组不同之处在于每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员(分量)项。
§ 9.2 结构体数组
9.2.1 定义结构体数组和定义结构体变量的方法相仿,只需说明其为数组即可。 例如:
struct student
{int num;char name[20];char sex;int age;
float score;char addr[30];
};struct student[3];
以上定义了一个数组 stu,数组有3个元素,均为 struct
student类型数据。
§ 9.2 结构体数组也可以直接定义一个结构体数组,例如,
struct student
{int num;
… };stu[3];
或:
strcut student
{int num;
… };stu[3];图 9-4
§ 9.2 结构体数组
9.2.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″}} ; 图 9-5
§ 9.2 结构体数组当然,数组的初始化也可以用以下形式:
struct student
{ int num;
…
};
struct student str[]{{… },{… },{… }};
即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。
结构体数组初始化的一般形式是在定义数组的后面加上
“={初值表列};”。
§ 9.2 结构体数组
9.2.3 结构体数组应用举例例 9.2对候选人得票的统计程序。设有 3个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。
#include <string.h>
#include <stdio.h>
struct person
{
char name[20];in count;
};
leader[3]={“Li”,0,”Zhang”,0,”Fun”,0}
例 9.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
§ 9.2 结构体数组程序定义一个全局的结构体数组 leader,它有3个元素,每一个元素包含两个成员
name(姓名)和 count(票数)。在定义数组时使之初始化,使 3位候选人的票数都先置零,
在主函数中定义字符数组 leader-name,它代表被选人的姓名,在 10次循环中每次先输入一个被选人的具体人名,然后把它与 3个候选人姓名相比,看它和哪一个候选人的名字相同。在输入和统计结束之后,将 3人的名字和得票数输出。 图 9-6
Li 0
Zhang 0
Fun 0
name count
§ 9.3 结构体指针结构体指针是指向结构体数据的指针,一个结构体变量的起始地址就是这个结构体变量的指针。指针变量既可以指向结构体变量,也可以用来指向结构体数组中的元素。但是,指针变量的基类型必须与结构体变量的类型相同。
例如:
struct student pt; /* pt可以指 向
struct student类型的数据 */
9.3.1 指向结构体变量的指针变量下面通过一个简单例子来说明指向结构体变量的指针变量的应用。
例 9.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
9.3.1 指向结构体变量的指针变量程序分析:
在函数的执行部分将结构体变量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
这样的形式。
图 9-7
9.3.1 指向结构体变量的指针变量以下 3种形式等价:
① 结构体变量.成员名
②( *p).成员名
③ p ->成员名其中 ->称为指向运算符。请分析以下几种运算:
p ->n得到p指向的结构体变量中的成员n的值。
p ->n++ 得到p指向的结构体变量中的成员n
的值,用完该值后使它加1。
++p ->n 得到p指向的结构体变量中的成员n
的值加1,然后再使用它。
9.3.2 指向结构体数组的指针
11.6.2 指向结构体数组的指针例 9.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
9.3.2 指向结构体数组的指针程序分析:
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了,不再执行循环。 图 9-8
9.3.2 指向结构体数组的指针注意:
(1) 如果p的初值为 stu,即指向第一个元素,
则p加1后 p就指向下一个元素。 例如,
(++p)->num 先使p自加1,然后得到它指向的元素中的 num成员值(即 10102)。
(p++)->num 先得到p ->num的值(即 10101)
,然后使p自加1,指向 stu[1]。
请注意以上二者的不同。
9.3.2 指向结构体数组的指针注意:
(2) 程序已定义了p是一个指向 struct student
类型数据的指针变量,它用来指向一个 struct
student类型的数据,不应用来指向 stu数组元素中的某一成员。
例如,p=stu[ 1].n ame ;
如果要将某一成员的地址赋给 p,可以用强制类型转换,先将成员的地址转换成 p的类型。
例如:p=(struct student *)
stu[ 0].n ame;
§ 9.3 结构体指针
9.3.3 结构体变量和指向结构体的指针作函数参数将一个结构体变量的值传递给另一个函数,有 3个方法,
(1) 用结构体变量的成员作参数 。
(2) 用结构体变量作实参 。
(3) 用指向结构体变量 ( 或数组 ) 的指针作实参,将结构体变量 ( 或数组 ) 的地址传给形参,
§ 9.3 结构体指针
11.6.2 指向结构体数组的指针例 9.5 有一个结构体变量 stu,内含学生学号、姓名和 3
门课程的成绩。要求在 main函数中赋予值,在另一函数
print中将它们输出。今用结构体变量作函数参数。
#include <stdio.h>
struct student
{
int num;
char name[20];
float score[3];
};
§ 9.3 结构体指针
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
例 9.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
§ 9.3 结构体指针程序分析:
此程序改用在定义结构体变量 stu时赋初值,这样程序可简化些。 print函数中的形参p被定义为指向 struct student类型数据的指针变量。注意在调用 print函数时,用结构体变量 str的起始地址& stu
作实参。在调用函数时将该地址传送给形参 p(p是指针变量)。这样p就指向 stu。在 print函数中输出
p所指向的结构体变量的各个成员值,它们也就是
stu的成员值,
main函数中的对各成员赋值也可以改用 scanf函数输入,图 9-9
§ 9.4 用指针处理链表
9.4.1 链表概述链表是一种常见的重要的数据结构,是动态地进行存储分配的一种结构。
链表的组成:
头指针:存放一个地址,该地址指向一个元素
结点:用户需要的实际数据和链接节点的指针图 9-10
§ 9.4 用指针处理链表用结构体建立链表:
struct student
{ int num;
float score;
struct student *next ; };
其中成员 num和 score用来存放结点中的有用数据(用户需要用到的数据),next
是指针类型的成员,它指向 struct
student类型数据(这就是 next所在的结构体类型)
图 9-11
§ 9.4 用指针处理链表
9.4.2 建立简单的静态链表例 9.7 建立一个如图 9.11所示的简单链表,它由 3个学生数据的结点组成。输出各结点中的数据。
#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
§ 9.4 用指针处理链表程序分析:
开始时使 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结点中的数据。
§ 9.4 用指针处理链表
9.4.3 建立动态链表所谓建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系例 9.8 写一函数建立一个有 3名学生数据的单向动态链表,
算法如图图 9-12
§ 9.4 用指针处理链表算法的实现:
我们约定学号不会为零,如果输入的学号为
0,则表示建立链表的过程完成,该结点不应连接到链表中。
如果输入的 p1->num不等于0,则输入的是第一个结点数据( n=1),令 head= p1,即把 p1的值赋给 head,也就是使 head也指向新开辟的结点 p1
所指向的新开辟的结点就成为链表中第一个结点图 9-13
§ 9.4 用指针处理链表算法的实现:
再开辟另一个结点并使 p1指向它,接着输入该结点的数据,
如果输入的 p1->num≠ 0,则应链入第2个结点
( n=2),将新结点的地址赋给第一个结点的
next成员,
接着使p2=p1,也就是使p2指向刚才建立的结点图 9-14
§ 9.4 用指针处理链表算法的实现:
再开辟一个结点并使 p1指向它,并输入该结点的数据,
在第三次循环中,由于n=3(n ≠ 1),又将p1的值赋给p2 ->next,也就是将第
3个结点连接到第2个结点之后,并使p2=
p1,使p2指向最后一个结点,
图 9-15
§ 9.4 用指针处理链表算法的实现:
再开辟一个新结点,并使 p1指向它,输入该结点的数据。由于 p1->num的值为0,不再执行循环
,此新结点不应被连接到链表中,
将 NULL赋给 p2->next.
建立链表过程至此结束,p1最后所指的结点未链入链表中,第三个结点的 next成员的值为 NULL,它不指向任何结点。
图 9-16
§ 9.4 用指针处理链表建立链表的函数如下,
#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为全局变量,本文件模块中各函数均可使用它
§ 9.4 用指针处理链表
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);
}
§ 9.4 用指针处理链表
9.4.4 输出链表首先要知道链表第一个结点的地址,也就是要知道 head的值 。 然后设一个指针变量 p,先指向第一个结点,输出p所指的结点,然后使p后移一个结点,再输出,直到链表的尾结点 。
图 9-17,9-18
§ 9.4 用指针处理链表例 9,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);
}
§ 9.4 用指针处理链表可以把例 9.7和例 9.9合起来加上一个主函数,组成一个程序,即,
#include <stdio.h>
#include <malloc.h>
#define LEN sizeof(struct student)
struct student
{long num;
float score; struct student *next;
};
int n;
§ 9.4 用指针处理链表
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;
§ 9.4 用指针处理链表
p2=p1;
p1=(struct student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
}
p2->next=NULL;
return(head);
}
void print(struct student head) /* 输出链表的函数 */
{struct student *p;
printf("\nNow,These %d records are:\n",n);
§ 9.4 用指针处理链表
p=head;
if(head!=NULL)
do
{printf("%ld %5.1f\n",p->num,p->score);
p=p->next;
}while(p!=NULL);
}
void main()
{struct student *head ;
head=creat();
print(head); /* 调用 print函数 */
}
§ 9.5 共用体类型
9.5.1 什么是共用体类型使几个不同的变量共占同一段内存的结构 称为
,共用体,类型的结构,
定义共用体类型变量的一般形式为:
union 共用体名
{
成员表列
} 变量表列;
图 9-19
§ 9.5 共用体类型例如:
union data union data
{ int i; { int i;
char ch; 或 char ch;
float f; float f;
} a,b,c; };union data a,b,c;
§ 9.5 共用体类型共用体和结构体的比较:
结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。
共用体变量所占的内存长度等于最长的成员的长度。
共用体和结构体的比较:
结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。
共用体变量所占的内存长度等于最长的成员的长度。
例如,上面定义的,共用体,变量a、b、c各占
4
个字节(因为一个实型变量占4个字节),而不是各占2+1+4=7个字节。
§ 9.5 共用体类型
9.5.2 共用体变量的引用方式只有先定义了共用体变量才能引用它,而且不能引用共用体变量,而只能引用共用体变量中的成员。
例如,前面定义了 a,b,c为共用体变量
a.i (引用共用体变量中的整型变量i)
a.ch(引用共用体变量中的字符变量ch)
a.f (引用共用体变量中的实型变量f)
§ 9.5 共用体类型
9.5.3 共用体类型数据的特点
(1)同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种。
(2) 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。
(3) 共用体变量的地址和它的各成员的地址都是同一地址。
§ 9.5 共用体类型
(4) 不能对共用体变量名赋值,也不能企图引用变量名来得到一个值,又不能在定义共用体变量时对它初始化。
(5) 以前的 C规定不能把共用体变量作为函数参数
,但可以使用指向共用体变量的指针作函数参数。 ANSI新标准放宽了限制,允许用共用体变量作为函数参数。
(6) 共用体类型可以出现在结构体类型定义中,
也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。
§ 9.5 共用体类型例 9.10 设有若干个人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、
性别、职业,班级 。教师的数据包括:姓名、号码、性别、职业,职务 。可以看出
,学生和教师所包含的数据是不同的。现要求把它们放在同一表格中。
图 9-10
§ 9.5 共用体类型
#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
9.5 共用体类型也可以不在结构体类型的声明中声明共用体类型,
而把它放在结构体类型的声明之前,即:
{int i;
union categ { int banji;
char position[10];
};
struct { int num;
char name[10];
char sex;
char job;
union categ category;}person[2];
§ 9.6 枚举类型枚举,将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
申明枚举类型用 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之一枚举元素枚举常量
§ 9.6 枚举类型说明:
(1)在C编译中,对枚举元素按常量处理,故称枚举常量。它们不是变量,不能对它们赋值。
(2) 枚举元素作为常量,它们是有值的,C语言编译按定义时的顺序使它们的值为0,1,2 …
(3) 枚举值可以用来作判断比较。
(4) 一个整数不能直接赋给一个枚举变量。
§ 9.6 枚举类型例1 1.1 3口袋中有红、黄、蓝、白、黑 5种颜色的球若干个。每次从口袋中先后取出3个球,问得到 3种不同色的球的可能取法,输出每种排列的情况。
算法:
图 9-22 9-23
§ 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
§ 9.7 用 typedef命名类型用 typedef声明新的类型名来代替已有的类型名声明 INTEGER为整型
typedef int INTEGER
声明结构类型
Typedef struct{
int month;
int day;
int year; }DATE;
§ 9.7 用 typedef命名类型声明NUM为整型数组类型
typedef int NUM[100];
声明STRING为字符指针类型
typedef char *STRING;
声明 POINTER为指向函数的指针类型,该函数返回整型值
typedef int (*POINTER)()
§ 9.7 用 typedef命名类型用 typedef定义类型的方法
① 先按定义变量的方法写出定义体(如,int i)。
② 将变量名换成新类型名(例如:将 i换成 COUNT)。
③ 在最前面加typedef
(例如,typedef int COUNT)。
④ 然后可以用新类型名去定义变量。
§ 9.7 用 typedef命名类型用 typedef定义类型的方法(举例)
① 先按定义数组变量形式书写,int n[100];
② 将变量名n换成自己指定的类型名:
int NUM[10 0];
③ 在前面加上 typedef,得到
typedef int NUM[100];
④ 用来定义变量,NUM n;
§ 9.7 用 typedef命名类型说明:
(1)用 typedef可以声明各种类型名,但不能用来定义变量。
(2) 用 typedef只是对已经存在的类型增加一个类型名,
而没有创造新的类型。
(3) 当不同源文件中用到同一类型数据时,常用
typedef声明一些数据类型,把它们单独放在一个文件中,然后在需要用到它们的文件中用 #include命令把它们包含进来。
(4) 使用 typedef有利于程序的通用与移植。
§ 9.7 用 typedef命名类型说明:
(5) typedef与 #define有相似之处,例如:
typedef int COUNT; #define COUNT int的作用都是用 COUNT代表 int。但事实上,它们二者是不同的。
#define是在预编译时处理的,它只能作简单的字符串替换,而 typedef是在编译时处理的。实际上它并不是作简单的字符串替换,而是采用如同定义变量的方法那样来声明一个类型