第 13章 结构体的应用
13.1 了解由用户构造的数据类型
13.2 结构体类型说明及结构体变量
13.3 结构体数组
13.4 函数之间结构体类型的数据传递
13.5 利用结构体变量构成静态链表
13.6 利用指针处理动态链表
13.1 了解由用户构造的数据类型
13.1.1 可以由用户构造的数据类型
13.1.2 用 typedef定义类型名
13.2 结构体类型说明及结构体变量
13.2.1 结构体类型的说明
13.2.2 结构体变量的定义
13.2.3 结构体变量的初始化
13.2.4 结构体变量中成员的访问
13.3 结构体数组从例 13.1中可以看出,利用结构体变量只能存放一名学生的信息 。 若要保存多名学生的信息就要使用结构体类型的数组 。
13.3.1 结构体数组的定义定义结构体数组的方法和定义结构体变量的方法一样:可以先说明结构体类型,
再用类型名定义数组;也可以在说明类型的同时定义数组 。 例如:
struct student
{ int num;
char name[9];
char sex;
struct date birthday;
float score[3];
};
struct student pers[3];
也可以采用以下形式:
typedef struct
{ int num;
char name[9];
char sex;
struct { int year,month,day ;} birthday;
float score[3];
}STU;
STU pers[3];
以上两种形式都是先说明了类型名
( struct student或 STU),再用类型名定义了具有 3个元素的结构体数组 pers。
若要直接定义结构体数组,可以采用以下两种形式:
结构体数组 pers中的每个元素都是一个结构体类型,如图 13-3所示 。 它们在内存中也占据着连续的存储单元 。
图 13-3 数组 pers 的结构示意图
13.3.2 结构体数组的初始化和其他类型的数组一样,结构体数组也可以在定义的同时进行初始化 。 例如:
struct student pers[3]={
{1,"Zhanghua",'M',1961,10,8,76.5,78.0,82.0},
{2,"Wangwei",'F',1960,12,20,70.0,85.5,76.0},
{3,"Liming",'M',1961,3,16,80.0,84.5,91.0} };
可以看出:所赋初值是放在一对花括号中的 。 为了清晰,每个元素的值又分别用一对花括号括起,中间以逗号分隔 。 数组 pers赋初值后的情况如图 13-4所示 。
图 13-4 数组 pers赋初值后的示意图在定义结构体数组时,也可以不指定元素的个数,而通过初值的个数决定数组的大小 。 形式如下:
struct student test[ ]={ {… },{… },
{… },{… } };
这时编译系统会根据初值的个数,判定出数组 test有 4个元素 。
13.3.3 结构体数组的应用结构体数组中有若干个元素,每个元素中都包含有各自的成员,应该采用什么样的形式去引用它们呢? 事实上,只要把数组元素看作是带有下标的变量,就可以沿用 13.2.4中介绍的成员引用方法:
结构体变量名,成员名此处可理解为:
结构体数组元素,成员名结合图 13.4可以看出:
pers[0].num
表示的是数组第 1个元素中值为 1的
num成员
pers[1].sex
表示的是数组第 2个元素中值为 F的
sex成员
pers[2].birthday.year
表示的是数组第 3个元素中的结构体
birthday中的 year成员
13.4 函数之间结构体类型的数据传递
13.4.1 结构体变量的成员作实参结构体变量的成员作实参时,参数的传递情况取决于该成员本身的数据类型 。
如果作为实参传送的这一成员是简单变量,
相应的形参应该是同类型的简单变量;如果作为实参传送的成员是数组名,对应的形参就必须是基类型相同的指针变量 。
例 13.4 结构体变量的成员作实参 。
#include <stdio.h>
typedef struct
{ int num;
char name[9];
char sex;
struct { int year,month,day ;} birthday;
float score[3];
}STU;
void fun1(char *name)
/* 形参为字符型指针 */
{ printf("%9s",name); }
void fun2(int y)
/* 形参为整型变量 */
{ printf("%5d",y); }
void fun3(float *s)
/* 形参为单精度型指针 */
{ int i;
for(i=0; i<3; i++)
printf("%5.1f",s[i]);
printf("\n");
}
main( )
{ STU
std={ 1,"Zhanghua",'M',1961,10,8,76.5,78.
0,82.0 };
fun1(std.name);
/* 相当于字符型数组名作实参 */
fun2(std.birthday.year);
/* 相当于整型变量作实参 */
fun3(std.score);
/* 相当于单精度型数组名作实参 */
}
程序的输出结果是:
Zhanghua 1961 76.5 78.0 82.0
13.4.2 结构体变量作形参结构体变量可以作为一个整体传递给相应的形参 。 根据,按值,传递的原则,
这时传给形参的是结构体变量的值 。 因此,
形参必须是同类型的结构体变量 。 系统将为形参开辟临时存储单元,用以存放结构体中各成员的值 。
例 13.5 结构体变量作形参 。
以下程序通过定义并赋初值的方式,
利用结构体变量存储了一名学生的信息,
通过调用函数 show输出其内容 。
源程序如下:
#include <stdio.h>
typedef struct
{ int num;
char name[9];
char sex;
struct { int year,month,day ;} birthday;
float score[3];
}STU;
void show(STU tt)
/* 形参是同类型的结构体变量 */
{ int i;
printf("%d %s %c %d–%d–%d",
tt.num,tt.name,tt.sex,
tt.birthday.year,tt.birthday.month,tt.birthday.day);
for(i=0; i<3; i++)
printf("%5.1f",tt.score[i]);
printf("\n");
}
main( )
{ STU std={
1,"Zhanghua",'M',1961,10,8,76.5,78.0,82.0
};
show(std); /* 结构体变量作实参 */
}
程序的运行结果如下:
1 Zhanghua M 1961-10-8 76.5 78.0 82.0
13.4.3 结构体变量的地址作实参如果需要在某个函数中改变实参的值,
就一定要传地址 。 当实参是结构体变量的地址时,对应的形参应该是同一结构体类型的指针变量 。
例 13.6 在例 13.5的基础上增加一个函数
modify,用以将学生的各科成绩乘以一个系数,
然后再调用 show函数输出 。
源程序如下:
#include <stdio.h>
typedef struct
{ int num;
char name[9];
char sex;
struct { int year,month,day ;} birthday;
float score[3];
}STU;
void show(STU tt)
{ int i;
printf("%d %s %c %d–%d–%d",
tt.num,tt.name,tt.sex,
tt.birthday.year,tt.birthday.month,tt.birthd
ay.day);
for(i=0; i<3; i++)
printf("%5.1f",tt.score[i]);
printf("\n");
}
void modify(STU *ss,float a)
/* 形参 ss为同类型的指针 */
{ int i;
for(i=0; i<3; i++)
ss–>score[i]*=a;
/* 用指针 ss引用结构体成员 */
}
main( )
{ STU std={
1,"Zhanghua",'M',1961,10,8,76.5,78.0,82.0
};
float a;
printf("请输入系数值,");
scanf("%f",&a);
modify(&std,a);
/* 结构体变量的地址作实参 */
printf("修改后的学生数据如下,\n");
show(std);
}
程序运行情况如下:
请输入系数值,0.8?
修改后的学生数据如下:
1 Zhanghua M 1961-10-8 61.2 62.4 65.6
13.4.4 结构体数组名作实参结构体数组名作实参,传递的是数组的首地址,相应的形参应该是同一结构体类型的指针 。 这一情况同 8.4.2介绍的一维数组名作实参是类似的,区别仅在于结构体数组中的每个元素都是一个结构体类型的数据 。
例 13.7 计算平均分,输出成绩单 。
设有如下定义:
typedef struct
{ int num;
char name[9];
float score[3];
float ave;
}STD;
编写函数 void fun1(STD s[ ],int n),
用来计算 s所指数组中每位学生 3门课的平均分,存入相应的 ave成员中 。 形参 n代表学生人数 。
编写函数 void fun2(STD x[ ],int n) 输出成绩单 。
在主函数中,利用四名学生的信息进行调试 。
源程序如下:
void fun1(STD s[],int n)
{ int i,j; float sum;
for(i=0; i<n; i++)
/* 计算每位学生的平均分 */
{ sum=0.0;
for(j=0; j<3; j++)
sum=sum+s[i].score[j];
s[i].ave=sum/3;
}
}
void fun2(STD x[],int n)
{ int i,j;
printf(" num name scor1 scor2 scor3 ave\n");
/* 输出成绩单 */
for(i=0; i<n; i++)
{ printf("%3d %6s ",x[i].num,x[i].name);
for(j=0; j<3; j++)
printf("%5.1f ",x[i].score[j]);
printf("%5.1f\n",x[i].ave);
}
}
main( )
{ STD x[4]={ 1,"Limi",60,67,76,0.0,
2,"Mali",73,81,69,0.0,
3,"Qiyin",86,75,82,0.0,
4,"Jake",66,85,72,0.0};
/* 初始化时存放平均分的成员值为 0.0 */
fun1(x,4);
fun2(x,4);
}
程序的运行结果如下:
num name scor1 scor2 scor3 ave
1 Limi 60.0 67.0 76.0 67.7
2 Mali 73.0 81.0 69.0 74.3
3 Qiyin 86.0 75.0 82.0 81.0
4 Jake 66.0 85.0 72.0 74.3
13.5 利用结构体变量构成静态链表
13.5.1 构成单向链表的结点结构
13.5.2 静态链表
13.6 利用指针处理动态链表
13.6.1 动态链表的概念
13.6.2 动态生成和释放结点所需的函数
13.6.3 动态链表的建立和输出
13.6.4 链表中结点的删除
13.6.5 链表中结点的插入
13.1 了解由用户构造的数据类型
13.2 结构体类型说明及结构体变量
13.3 结构体数组
13.4 函数之间结构体类型的数据传递
13.5 利用结构体变量构成静态链表
13.6 利用指针处理动态链表
13.1 了解由用户构造的数据类型
13.1.1 可以由用户构造的数据类型
13.1.2 用 typedef定义类型名
13.2 结构体类型说明及结构体变量
13.2.1 结构体类型的说明
13.2.2 结构体变量的定义
13.2.3 结构体变量的初始化
13.2.4 结构体变量中成员的访问
13.3 结构体数组从例 13.1中可以看出,利用结构体变量只能存放一名学生的信息 。 若要保存多名学生的信息就要使用结构体类型的数组 。
13.3.1 结构体数组的定义定义结构体数组的方法和定义结构体变量的方法一样:可以先说明结构体类型,
再用类型名定义数组;也可以在说明类型的同时定义数组 。 例如:
struct student
{ int num;
char name[9];
char sex;
struct date birthday;
float score[3];
};
struct student pers[3];
也可以采用以下形式:
typedef struct
{ int num;
char name[9];
char sex;
struct { int year,month,day ;} birthday;
float score[3];
}STU;
STU pers[3];
以上两种形式都是先说明了类型名
( struct student或 STU),再用类型名定义了具有 3个元素的结构体数组 pers。
若要直接定义结构体数组,可以采用以下两种形式:
结构体数组 pers中的每个元素都是一个结构体类型,如图 13-3所示 。 它们在内存中也占据着连续的存储单元 。
图 13-3 数组 pers 的结构示意图
13.3.2 结构体数组的初始化和其他类型的数组一样,结构体数组也可以在定义的同时进行初始化 。 例如:
struct student pers[3]={
{1,"Zhanghua",'M',1961,10,8,76.5,78.0,82.0},
{2,"Wangwei",'F',1960,12,20,70.0,85.5,76.0},
{3,"Liming",'M',1961,3,16,80.0,84.5,91.0} };
可以看出:所赋初值是放在一对花括号中的 。 为了清晰,每个元素的值又分别用一对花括号括起,中间以逗号分隔 。 数组 pers赋初值后的情况如图 13-4所示 。
图 13-4 数组 pers赋初值后的示意图在定义结构体数组时,也可以不指定元素的个数,而通过初值的个数决定数组的大小 。 形式如下:
struct student test[ ]={ {… },{… },
{… },{… } };
这时编译系统会根据初值的个数,判定出数组 test有 4个元素 。
13.3.3 结构体数组的应用结构体数组中有若干个元素,每个元素中都包含有各自的成员,应该采用什么样的形式去引用它们呢? 事实上,只要把数组元素看作是带有下标的变量,就可以沿用 13.2.4中介绍的成员引用方法:
结构体变量名,成员名此处可理解为:
结构体数组元素,成员名结合图 13.4可以看出:
pers[0].num
表示的是数组第 1个元素中值为 1的
num成员
pers[1].sex
表示的是数组第 2个元素中值为 F的
sex成员
pers[2].birthday.year
表示的是数组第 3个元素中的结构体
birthday中的 year成员
13.4 函数之间结构体类型的数据传递
13.4.1 结构体变量的成员作实参结构体变量的成员作实参时,参数的传递情况取决于该成员本身的数据类型 。
如果作为实参传送的这一成员是简单变量,
相应的形参应该是同类型的简单变量;如果作为实参传送的成员是数组名,对应的形参就必须是基类型相同的指针变量 。
例 13.4 结构体变量的成员作实参 。
#include <stdio.h>
typedef struct
{ int num;
char name[9];
char sex;
struct { int year,month,day ;} birthday;
float score[3];
}STU;
void fun1(char *name)
/* 形参为字符型指针 */
{ printf("%9s",name); }
void fun2(int y)
/* 形参为整型变量 */
{ printf("%5d",y); }
void fun3(float *s)
/* 形参为单精度型指针 */
{ int i;
for(i=0; i<3; i++)
printf("%5.1f",s[i]);
printf("\n");
}
main( )
{ STU
std={ 1,"Zhanghua",'M',1961,10,8,76.5,78.
0,82.0 };
fun1(std.name);
/* 相当于字符型数组名作实参 */
fun2(std.birthday.year);
/* 相当于整型变量作实参 */
fun3(std.score);
/* 相当于单精度型数组名作实参 */
}
程序的输出结果是:
Zhanghua 1961 76.5 78.0 82.0
13.4.2 结构体变量作形参结构体变量可以作为一个整体传递给相应的形参 。 根据,按值,传递的原则,
这时传给形参的是结构体变量的值 。 因此,
形参必须是同类型的结构体变量 。 系统将为形参开辟临时存储单元,用以存放结构体中各成员的值 。
例 13.5 结构体变量作形参 。
以下程序通过定义并赋初值的方式,
利用结构体变量存储了一名学生的信息,
通过调用函数 show输出其内容 。
源程序如下:
#include <stdio.h>
typedef struct
{ int num;
char name[9];
char sex;
struct { int year,month,day ;} birthday;
float score[3];
}STU;
void show(STU tt)
/* 形参是同类型的结构体变量 */
{ int i;
printf("%d %s %c %d–%d–%d",
tt.num,tt.name,tt.sex,
tt.birthday.year,tt.birthday.month,tt.birthday.day);
for(i=0; i<3; i++)
printf("%5.1f",tt.score[i]);
printf("\n");
}
main( )
{ STU std={
1,"Zhanghua",'M',1961,10,8,76.5,78.0,82.0
};
show(std); /* 结构体变量作实参 */
}
程序的运行结果如下:
1 Zhanghua M 1961-10-8 76.5 78.0 82.0
13.4.3 结构体变量的地址作实参如果需要在某个函数中改变实参的值,
就一定要传地址 。 当实参是结构体变量的地址时,对应的形参应该是同一结构体类型的指针变量 。
例 13.6 在例 13.5的基础上增加一个函数
modify,用以将学生的各科成绩乘以一个系数,
然后再调用 show函数输出 。
源程序如下:
#include <stdio.h>
typedef struct
{ int num;
char name[9];
char sex;
struct { int year,month,day ;} birthday;
float score[3];
}STU;
void show(STU tt)
{ int i;
printf("%d %s %c %d–%d–%d",
tt.num,tt.name,tt.sex,
tt.birthday.year,tt.birthday.month,tt.birthd
ay.day);
for(i=0; i<3; i++)
printf("%5.1f",tt.score[i]);
printf("\n");
}
void modify(STU *ss,float a)
/* 形参 ss为同类型的指针 */
{ int i;
for(i=0; i<3; i++)
ss–>score[i]*=a;
/* 用指针 ss引用结构体成员 */
}
main( )
{ STU std={
1,"Zhanghua",'M',1961,10,8,76.5,78.0,82.0
};
float a;
printf("请输入系数值,");
scanf("%f",&a);
modify(&std,a);
/* 结构体变量的地址作实参 */
printf("修改后的学生数据如下,\n");
show(std);
}
程序运行情况如下:
请输入系数值,0.8?
修改后的学生数据如下:
1 Zhanghua M 1961-10-8 61.2 62.4 65.6
13.4.4 结构体数组名作实参结构体数组名作实参,传递的是数组的首地址,相应的形参应该是同一结构体类型的指针 。 这一情况同 8.4.2介绍的一维数组名作实参是类似的,区别仅在于结构体数组中的每个元素都是一个结构体类型的数据 。
例 13.7 计算平均分,输出成绩单 。
设有如下定义:
typedef struct
{ int num;
char name[9];
float score[3];
float ave;
}STD;
编写函数 void fun1(STD s[ ],int n),
用来计算 s所指数组中每位学生 3门课的平均分,存入相应的 ave成员中 。 形参 n代表学生人数 。
编写函数 void fun2(STD x[ ],int n) 输出成绩单 。
在主函数中,利用四名学生的信息进行调试 。
源程序如下:
void fun1(STD s[],int n)
{ int i,j; float sum;
for(i=0; i<n; i++)
/* 计算每位学生的平均分 */
{ sum=0.0;
for(j=0; j<3; j++)
sum=sum+s[i].score[j];
s[i].ave=sum/3;
}
}
void fun2(STD x[],int n)
{ int i,j;
printf(" num name scor1 scor2 scor3 ave\n");
/* 输出成绩单 */
for(i=0; i<n; i++)
{ printf("%3d %6s ",x[i].num,x[i].name);
for(j=0; j<3; j++)
printf("%5.1f ",x[i].score[j]);
printf("%5.1f\n",x[i].ave);
}
}
main( )
{ STD x[4]={ 1,"Limi",60,67,76,0.0,
2,"Mali",73,81,69,0.0,
3,"Qiyin",86,75,82,0.0,
4,"Jake",66,85,72,0.0};
/* 初始化时存放平均分的成员值为 0.0 */
fun1(x,4);
fun2(x,4);
}
程序的运行结果如下:
num name scor1 scor2 scor3 ave
1 Limi 60.0 67.0 76.0 67.7
2 Mali 73.0 81.0 69.0 74.3
3 Qiyin 86.0 75.0 82.0 81.0
4 Jake 66.0 85.0 72.0 74.3
13.5 利用结构体变量构成静态链表
13.5.1 构成单向链表的结点结构
13.5.2 静态链表
13.6 利用指针处理动态链表
13.6.1 动态链表的概念
13.6.2 动态生成和释放结点所需的函数
13.6.3 动态链表的建立和输出
13.6.4 链表中结点的删除
13.6.5 链表中结点的插入