配色: 字号:
《C语言程序设计》第9章 结构体、共用体和用户定义类型 写字字帖
2023-05-24 | 阅:  转:  |  分享 
  
第9章 结构体、共用体和用户定义类型 前言9.1 结构体 9.2 共用体 9.3 用指针处理链表 9.4 用typedef声明新类型名 前
言问题:一个学生的信息包括姓名(char型)、性别(char型)、年龄(int型)、学号(int型)、成绩(float型)等。
我们如果分别定义互相独立的简单变量,显然难以反映这些数据信息的内在联系。而数据类型不同,也不能通过数组来存放。C语言允许用户根据需
要自己建立一些数据类型,比如可将上述一个学生的不同类型的数据信息共同存放于构造类型——结构体或者共用体中,同时,C语言也允许使用如
typedef等来定义说明一个新的数据类型。9.1 结构体 9.1.1 结构体类型数据的定义 9.1.2 结构体类型数据成员的引用
9.1.3 结构体数组 9.1.4 结构体指针 9.1.1 结构体类型数据的定义 用户建立由不同类型数据组成的组合型的数据结构,
称之为结构体类型,组成结构体类型的每一个数据称之为该结构体类型的成员。在使用结构体类型时,首先要对结构体类型的组成进行说明,称之为
结构体类型的定义。格式:struct 结构体标识名 { 数据类型 结构体成员名1; 数据类型 结构体成员名2
; … 数据类型 结构体成员名n; };其中:“struct”为定义结构体类型的关键字。例如:以上学生的信息可定义
为下列一个名为Student的结构体类型:struct Student // S
tudent为结构体标识名 { char name[20]; //每个成员项以分号(;)
结束 char sex; int age; int num; float score; };
//结构体定义以分号(;)结束以上定义中,结构体类型Student由5个成员组成。
9.1.1 结构体类型数据的定义结构体类型的成员也可以包含另一个结构体类型,形成结构类型嵌套。例如:struct Date {
int month; int day; int year; };struct Student { char na
me[20]; char sex; struct Date birthday; float score; int
num; };struct Student类型的结构如右图: 9.1.1 结构体类型数据的定义当结构体类型定义
后,就可以指定该结构体类型的具体对象,即定义结构体类型的变量,称之为结构体变量。定义结构体变量可采取以下三种形式: 1.声明结构体
标识名后,再定义结构体变量2.声明结构体标识的同时定义结构体变量3.不指定结构体标识名而直接定义结构体变量 9.1.1 结构体
类型数据的定义1.声明结构体标识名后,再定义结构体变量格式:struct 结构体标识名 结构体变量名表例如:在前面声明了结构体类型
Student后,再来定义结构体变量stu1和stu2。struct Student stu1,stu2;这种形式和定义其它类型变
量形式相似(如int a,b;)。变量stu1和stu2则具有Student类型的数据结构,是由多个数据成员组成的构造类型的变量,
如下图所示: 在定义了结构体变量后,系统会为之分配内存单元,stu1和stu2分别占据一段连续的存储单元,在Visual C++环
境中,stu1和stu2分别占据33字节(name 20个字节,sex 1个字节,age 4个字节,num 4个字节,score
4个字节)。9.1.1 结构体类型数据的定义2.声明结构体标识的同时定义结构体变量格式:struct 结构体标识名 {
数据类型 结构体成员名1; 数据类型 结构体成员名2; … 数据类型 结构体成员名n; } 结构体变量
名表;例如:在前面声明了结构体类型Student的同时定义结构体变量stu1和stu2。struct Student
{ char name[20]; char se
x; int age; int num; float score; } stu1,stu2; 9.1.1 结构体
类型数据的定义3.不指定结构体标识名而直接定义结构体变量 格式:struct
//不需要指定结构体标识名 { 数据类型 结构体成员名1; 数据类型 结构体成员名2; …
数据类型 结构体成员名n; } 结构体变量名表;例如:采用此方法定义结构体变量stu1和stu2。struct
{ char name[20]; char
sex; int age; int num; float score; } stu1,stu2; 【注
意】此方法指定的是个无名的结构体类型,所以该结构体类型不能再定义其它变量。【说明】① 结构体类型与结构体变量是不同的概念,不要混同
。只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。② 结构体变量中的成员可以单独使用,运算方式和普通变量相同。③ 结
构体类型中的成员名可以与程序中的变量名相同,但二者不代表同一对象。9.1.1 结构体类型数据的定义例9.1 下面结构体的定义语句中
,错误的是(2009年9月全国计算机等级考试二级C试题选择题第36题)A)struct ord {int x;int y;int
z;}; struct ord a;B)struct ord {int x;int y;int z;} struct ord a;
C)struct ord {int x;int y;int z;} a;D)struct {int x;int y;int z;}
a;分析: A)采用的先声明结构体类型,再定义结构体变量的方式,格式正确;C)采用的声明结构体类型的同时定义结构体变量的方式,格
式正确;D)采用的不指定结构体类型而直接定义结构体变量的方式,格式正确;B)在声明结构体类型后没有用分号结束,选B。返回9.1.2
结构体类型数据成员的引用在定义了结构体变量后,就可以引用该结构体变量,执行赋值、存取和运算等操作。在引用结构体变量时,主要是通过
引用结构体变量的各个成员来实现各种操作。引用结构体变量的成员的方式为:格式:结构体变量名.成员名9.1.2 结构体类型数据成员的引
用例9.2 把一个学生的信息(包括姓名、性别、年龄、学号、成绩)放在一个结构体变量中,然后输出这个学生的信息。#include <
stdio.h>int main(){ struct Student //
声明Student为结构体类型 {char name[20]; //以下五行为结构体
的成员 char sex; int age; int num; float scor
e; }a={“Li Lin”,‘M’,18,10101, 86.5 }; //定义结构体变量a并初始化 printf
("name:%s\nsex:%c\nage:%d\nNO.:%d\nscore:%4.1f\n", a.na
me,a.sex,a.age,a.num,a.score); return 0; }运行结果:name:Li Linsex:Ma
ge:18NO.:10101score:86.59.1.2 结构体类型数据成员的引用【例9.2说明】①“.”是成员运算符,它在所有
运算符中优先级最高。可认为a.XX为一个整体。② 在定义结构体变量的同时可以对它的成员初始化,初始化方式和数组的初始化类似,用花括
号括起来一些常量,这些常量依次对结构体变量中的各成员赋值。 ③ 在引用结构体变量时,不能将一个结构体变量作为一个整体处
理,而应当通过对该结构体变量的各个成员项的引用实现相应操作。若上述例题中输出写成printf("name:%s\nsex:%c\n
age:%d\nNO.:%d\nscore:%4.1f\n", a) 错误。④ 可以引用结构体成员地址和结构体变量地址。如:sca
nf(“%d“,&a.num); 作用是从键盘为成员a.num提供值。printf(“%o“,&a);作用是输出a的首地址。但不能
用以下方法整体读入结构体变量:scanf(”%s,%c,%d,%d,%f“,&a)。 ⑤ 若成员本身又属于一个结构体类型,只能对最
低级的成员进行赋值、存取以及运算。如:stu1.birthday.year=1986⑥ 结构体变量成员可以象普通变量一样进行各种运
算,如:stu1.age= stu2.age(赋值运算);sum==stu1.age+stu2.age (加法运算)。同类的结构体
变量可以互相赋值,如 stu1=stu2。9.1.2 结构体类型数据成员的引用例9.3 设有定义:(2009年9月全国计算机等级考
试二级C试题填空题第12题)struct person{ int ID;char name[12];}p;请将scanf(“%d”
, );语句补充完整,使其能够为结构体变量p的成员ID正确读入数据。分析: 在对结构体成员提供值时,应分别对各成员按变
量方式处理。此题中,变量p的成员ID为int型数据,输入时,把p.ID看做一个整体,引用其地址,所以输入格式为:scanf(“%d
”,&p.ID)。例9.4 设有定义:(2011年3月全国计算机等级考试二级C试题选择题第36题)struct {char mar
k[12];int num1;double num2;} t1,t2;设有若变量均已正确赋初值,则以下语句中错误的是A)t1=t2
; B)t2.num1=t1.num1;C)t2.mark=t1.mark; D)t2.num2=t1.num
2;分析:定义为同类型的结构体变量可互相赋值,A)正确;结构体变量成员和普通变量的运算规则相同,所以整型数据、双精度实数型数据都可
直接赋值,B)、D)正确。用赋值语句将一个字符数组直接给一个字符数组是不合法的,C)错误。选C9.1.2 结构体类型数据成员的引用
例9.5 有以下程序:(2010年3月全国计算机等级考试二级C试题选择题第37题)#include #inclu
de struct A //定义结构体类型A{ int a; char
b[10]; double c;};void f(struct A t); //定义函数fmain(){ struct A
a={1001,"ZhangDa",1098.0};//定义结构体变量a并进行初始化操作f(a);
//调用f函数,结构体变量a作为实参printf("%d,%s,%6.1f\n",a.a
,a.b,a.c);}void f(struct A t){ t.a=1002; strcpy(t.b,"ChangRong");
t.c=1202.0;}程序运行后的输出结果是A)1001,zhangDa,1098.0 B)1002,changRong,12
02.0C)1001,ehangRong,1098.0 D)1002,ZhangDa,1202.09.1.2 结构体类型数据成员
的引用例9.5分析:本题中函数调用的形式为传值调用,不会改变任何值。所以输出结果仍是结构体变量a原来的成员的初始值。选A。【说明】
将一个结构体变量的值传递给另一个函数有三种形式:① 结构体变量的成员作为参数,如用a.a作为实参,将实参值传递给形成。用法同普通变
量作实参相同,是“按值传递”。需要保持实参和形参类型一致。②用结构体变量作为实参,同样采用“按值传递”的方式,不过是将结构体变量所
有成员值按顺序传递给形参。注意形参和实参的结构类型相同,但运行时占用不同的存储空间,调用时,被调函数不能修改调用函数实参的值。③用
结构体指针变量作为实参,即将结构体变量的首地址传递给形参,即“按地址传递”。此时,实参和形参所指向的是同一组内存单元。返回9.1.
3 结构体数组结构体数组就是指数组中的每一个数组元素都是结构体类型变量。定义结构体数组方式和定义结构体变量相同。格式:struct
结构体名 {结构体成员表列} 数组名【数组长度】也可以先声明结构体类型,如struct Person,然后再用此类型定义结构体数
组:结构体类型 数组名【数组长度】结构体数组初始化形式和数组赋初值的方式相同。在定义数组的后面加上:={初值表列};由于数组中的每
个元素都是一个结构体变量,通常将一个数组元素的成员放在一对花括号中,以区分各个元素。结构体数组的引用指对结构体数组元素的引用,引用
结构体变量的方法适用于结构体数组元素。格式:数组元素名称.成员名结构体数组元素可赋值给同一结构体数组中的另一个元素,或者赋值给同类
型的变量。9.1.3 结构体数组例9.6 有以下程序:(2011年3月全国计算机等级考试二级C试题选择题第38题)#include
struct S{ int a,b;}data[2]={10,100,20,200};void main(){
struct S p=data[1]; printf("%d\n",++(p.a));}程序运行后的输出结果是A)10 B)1
1 C)20 D)21分析:定义结构体数组变量data赋初值后,相当于data[0].a=10,data[0].b=100,dat
a[1].a=20,data[1].b=200;将data[1]赋值给结构体变量p,即p.a=20,p.b=200。所以,++(p
.a)的值为21,选D9.1.3 结构体数组例9.7 输入三个学生的信息,包括姓名,学号,性别,成绩,输出成绩在90分以上(含90
)的男生信息#include struct student //声明结构体类型
student{ char xm[20]; int xh; char xb; float cj;};void mai
n(){ struct student stu[3]; //声明结构体数组 int i; for(i=0;i<
3;i++) { scanf("%s%d,%c,%f",stu[i].xm,&stu[i].xh,&stu[i].xb,&s
tu[i].cj); //用scanf函数输入每个学生的姓名、学号、性别、成绩 }
for(i=0;i<3;i++) if(stu[i].xb==''m''&&stu[i].cj>=90.0) //判定性别和成
绩 printf("%d\t%s\t%4c\t%5.1f\n",stu[i].xh,stu[i].xm,stu[i].xb,
stu[i].cj); //按学号、姓名、性别、成绩的顺序输出学生信息}9.1.3 结构体数组例9.7运行结果:liyan
g<回车>001,m,92.5<回车>wangling<回车>1002,f,95<回车>chenyang<回车>1003,m,86
.5<回车>1001 liyang m 92.5【注意】本例中用scanf函数输入各成员时,字符数组以回车作为字符串的输入结
束,其它变量都用“,”隔开。返回9.1.4 结构体指针指向结构体的指针称为结构体指针变量。一个结构体变量的起始地址就是这个结构体变
量的指针,结构体指针也可以指向结构体数组中的元素,即将结构体数组的起始地址赋给指针变量。结构体指针变量定义方式如下格式:struc
t 结构体类型 结构体指针例如:struct Student p; //p可以指向struct Student类型的变量或者
数组元素9.1.4 结构体指针例9.8 有以下定义和语句(2010年3月全国计算机等级考试二级C试题选择题第38题)struct
workers //声明结构体类型struct workers{ int n
um;char name[20];char c;struct {int
day; int month; int year;}s;  //成员s为另一个结构体类型}; struct workers w,
pw;//定义struct workers类型变量w,以及指向struct workers类型数据的指针变量pwpw=&w;
//pw指向w,即将结构体变量w的起始地址赋给指针变量w能给w中year成员赋值1980的语句是A)
pw.year=198O; B)w.year=1980;C)pw->year=1980; D)w.s.year=1980;9.1
.4 结构体指针例9.8分析:year属于结构体类型struct workers的成员结构体类型s,所以要访问到year需要逐级访
问,访问方式为w.s.year.。D选项可以正确赋值。对year赋值,也可采取以下方式:(pw).s.year=1980;pw-
>s->year=1980【说明】① (pw)表示pw指向的结构体变量,(pw).s.year则表示pw指向的结构体成员s的成
员year。由于成员运算符“.”优先于“”,pw两侧括号不能省。② 为直观方便,C语言允许把“(pw).”形式用“pw->”
形式替代,“->”称为指向运算符,用于连接指针变量与其指向的结构体变量的成员。所以,如果pw指向结构体变量w,以下3种用法等价:1
)w.成员名(如w.num)2)(pw) .成员名(如(pw) .num)3)pw->成员名(如pw->num)③ 指向运算符
“->”优先级最高,例如++p->num等同于++ (p->num),即对p->num的值做自增运算。9.1.4 结构体指针例9.
9 有以下程序(2011年3月全国计算机等级考试二级C试题选择题第37题)#include struct ord
  //定义结构体类型struct ord{int x,y;}dt[2]={1,2
,3,4}; //定义结构体数组dt并对数组初始化main(){ struct ord p=dt;
//定义指向struct ord类型数据的指针变量p,并且p指向结构体数组dt的第一个元素的首地址   printf("
%d,",++(p->x)); printf("%d\n",++(p->y)); } 程序运行后的输出结果是A)1,2 B)4,
1 C)3,4 D)2,39.1.4 结构体指针例9.9分析:此题中,定义的结构体数组dt有两个元素,每个元素包含两个成员x和
y。初始化值表示dt[0].x=1,dt[0].y=2,d[1].x=3,dt[1].y=4,p指向数组dt的第一个元素,即p->
x=1,p->y=2,所以,++(p->x)=2,++(p->y)=3。选D【说明】①由于指向运算符“->”优先级最高,++(p-
>x)等价于 ++p->x。②但如果要求输出“(++p)->x”的值,则表示先使p增加1,此时,p指向的是数组的第2个元素的x成员
值,输出结果为3,,指针p的变化如图9.4 指针p的变化示意图9.2 共用体 9.2.1 共用体类型数据的定义 9.2.2 共用体
类型数据成员的引用 9.2.1 共用体类型数据的定义C语言中,共用体类型数据和结构体类型数据都属于构造类型。共用体类型数据在定义上
与结构体相似。区别在于,结构体变量是各类型数据成员的集合,每个成员占用不同的存储空间,结构体变量所占内存是各成员占的内存和。而共用
体变量所有成员共享同一段内存单元,以最长的成员所占的内存作为共用体变量的存储空间;所有成员从同一地址开始存储,采用覆盖技术,在某一
时刻只能让一个成员起作用。 9.2.1 共用体类型数据的定义定义共用体类型可采取以下方式:格式:union 共用体标识名{ 数
据类型1 共用体成员名 1; 数据类型2 共用体成员名 2; … 数据类型n 共用体成员名 n;};例如:union
Data{int a;char c;float x;} ;9.2.1 共用体类型数据的定义共用体变量的定义和结构体变量的定义相似,
可采用以下三种形式:1.声明共用体类型名后,再定义共用体变量2.声明共用体标识的同时定义共用体变量3.不指定共用体标识名而直接定义
共用体变量9.2.1 共用体类型数据的定义1.声明共用体类型名后,再定义共用体变量例如:union Data{int a;char
c;float x;};union Data m,n;9.2.1 共用体类型数据的定义2.声明共用体标识的同时定义共用体变量例如
:union Data{int a;char c;float x;} m,n;9.2.1 共用体类型数据的定义3.不指定共用体标识
名而直接定义共用体变量例如:union {int a;char c;float x;} m,n;9.2.1 共用体类型数据的定义以
上几种定义形式中,共用体m和n等价,共用体m和n所占存储空间相同。VC环境中,存储空间分配方式如下图所示:共用体变量m(n)在内存
中所占字节数是成员中最长的(整型和实型都是4个字节)4个字节;如果定义的是结构体变量,所占内存则为(4+1+4)=9个字节。 返回
9.2.2 共用体类型数据成员的引用在定义了共用体变量后才能引用它,引用形式为引用共用体变量的成员,一般不能整体引用共用体变量(C
99标准中有例外)。引用共用体变量的一个成员方式为:格式:共用体变量名.成员名如在前面定义了m、n为共用体变量,可以引用其成员:m
.c (引用共用体变量m中的字符变量c)【注意】C99标准中允许同类型的共用体变量互相赋值。如:m=n9.2.2 共用体类型数据
成员的引用定义共用体变量的同时,只能对第一个成员的类型进行初始化。一般格式:union 共用体标识名 共用体变量={第一个数据成员
的类型值};例如:union Data{int a;char c;float x;} m={6,‘a’,3.5} //错误,不
能初始化三个成员,它们占用同一段内存union Data m={6}; // 正确,对共用体第一个成员整型a赋
初值 union Data m={.c=‘M’}; //C99允许对指定的成员赋初值【说明】① 共用体类型在同一内存单元可以存
放不同类型的数据,但在每一瞬间只能存放其中一个成员,即共用体变量只能存放一个值。9.2.2 共用体类型数据成员的引用例如:unio
n Data{ int a;char c;float x;}m;m.a=97; //将97按整数形式存放在共用体变量中,在
内存中最后一个字节存储形式为“01100001”执行输出语句:printf(”%d”,m.a); //输出整数97。将存储信息按
整数形式处理printf(”%c”,m.c); //输出字符’a’。 将存储信息按字符形式处理printf(”%f”,m.x)
; //输出实数0.000000。将存储信息按浮点数形式处理② 共用体存储空间保留的是最后一次被赋值的成员的值。例如执行以下赋值
语句:m.c=’M’; m.f=3.5; m.a=6;共用体变量存放的是最后输入的6,在引用时应特别注意当前共用体变量的存储成员。
③ 共用体变量的地址和它各成员的地址都是同一地址,都是从共用体变量空间的起点开始。④ 共用体类型可以出现在结构体类型定义中,数组也
可以是共用体的成员。⑤ 以前的C不能使用共用体变量作为函数参数,也不能使用函数返回共用体变量,但可以用指向共用体变量的指针。C99
允许共用体变量作为函数参数 9.2.2 共用体类型数据成员的引用例9.10 分析下列程序的运行结果#include h>union //声明共用体类型{int i; //以下两行指定共用体
成员 char c[2]; }a; //声明共用体变量aint main(){ch
ar x; //定义字符变量xa.i=259; //对共用体变量成员整型i赋初值x=
a.c[0]; //以下三行交换数组元素a.c[0]和a.c[1]的值 a.c[0]=a.c[1];
a.c[1]=x; printf("%d\n",a.i);return 0;}运行结果:769分析:共用体变量成员a.i
赋初值259,在共用体变量的内存中存储方式为“0000000100000011”,交换a.c[0]和a.c[1]的值后,内存的存储
变为“0000001100000001”,转换成十进制输出为769。9.3 用指针处理链表 9.3.1 什么是链表 9.3.2 建
立简单的静态链表 9.3.3 建立动态链表 9.3.4 结点数据的输出、删除与插入 9.3.1 什么是链表在前面的学习中,我们认识
到数组是一批有先后次序的元素所组成的序列。在使用数组存放数据时,必须先定义固定的数组长度,而当数组元素不确定时,需要开辟足够大的存
储空间,显然这会造成内存空间的浪费。并且在数组中,插入或删除元素时,需要移动其它元素。链表是一种常见而重要的数据结构类型。链表和数
组类似,也是一批有先后次序的序列。但是链表中的元素可动态的分配存储单元,并且链表中的各元素在内存中的地址可以不连续,所以在链表中插
入和删除元素时也不需要移动其它元素。链表中,每一个元素称为结点。结点是链表的基本存储单位,每个结点之间可以占用不连续的内存。结点与
结点之间通过指针链接在一起,一个元素指向下一个元素,直到最后一个元素,该元素不再指向其它元素,称之为“表尾”,“表尾”的地址存放一
个空指针(NULL)。所以,每个结点至少应包括两部分:用户需要用的实际数据和下一个结点的地址(用指针变量存放)。9.3.1 什么是
链表如图9.6为最简单的一种链表(单向链表)的结构。如图所示,链表有一个“头指针”变量(图中以head表示),它是指向链表表头结点
的指针。每个结点都包含数据信息(图中的A、B、C、D)和下一结点的地址(图中的1259、1326等)两部分信息。可见,链表结构中,
逻辑相连的数据,其结点间的物理地址并不连续。要访问某个元素,需要先找到它的上个元素提供的地址,逐个往前,所以最终需要通过“头指针”
(head)才可以访问整个链表中的元素。 9.3.1 什么是链表由于结点可包含不同的数据类型,根据前面所学,定义结点应采用结构体类
型。一般形式:struct node{int data; //用户需要用的实际数据(数据域),注意不仅
仅是整型struct nodenext //下一个结点的地址(指针域)}9.3.1 什么是链表例如:str
uct Student //定义结构体类型Student{char name[20]; //n
ame和score存放用户要用的数据 float score;struct Student next; //next是指
针变量,指向结构体变量}每个结点都属于struct Student类型,next是struct Student类型的成员,同时又指
向struct Student类型的数据,通过此方法,可建立链表。链表存储具有以下特点:1)插入、删除结点时,只用改变结点中指针域
的值即可。2)查找操作需要从“头指针”开始顺序查找,不适合有频繁查找操作的表。3)可实现动态分配和扩展空间(需用到和动态内存分配相
关的函数:malloc、calloc、realloc、free等)。返回9.3.2 建立简单的静态链表所有结点都在程序中定义,不临
时开辟,也不能用完后释放,这种链表称为静态链表。例9.11 在以下程序(2011年9月全国计算机等级考试二级C试题选择题第37题)
#includemain(){struct node //声明结构体类型struct no
de{int n; struct node next; //next为指针变量,指向结构体变量}p;
struct node x[3]={{2,x+1},{4,x+2},{6,NULL}};//定义结构体数组p
=x; //p指向数组x的第一个成员printf(“%d,”,p->n);printf(“%
d\n”,p->next->n);}程序运行后的输出结果是A)2,3 B)2,4 C)3,4 D)4,69.3.2 建立简单的
静态链表例9.11分析:此题中,结构体数组初始化值后相当于:x[0].n=2,结点x[1]的起始地址赋给x[0]的next成员;x
[1].n=4,结点x[2]的起始地址赋给x[1]的next成员; x[2].n=6,x[2]的next成员不存储其它结点地址,这
样就形成了链表。“p->n”,表示p指向的结点的数据,因为p指向的是数组的x的第一个元素x[0],所以p->n的值为2。“p->n
ext”,表示p指向下一个结点。p->next指向的是x+1,即指向的是数组x的第二个元素x[1],所以,p->next->n的值
为4。所以选B。返回9.3.3 建立动态链表所谓建立动态链表,是指程序执行过程中一个一个的开辟结点和输入各结点数据,并建立起前后链
接的关系。建立过程中,要首先定义一个包含数据域和指针域的结构类型,然后定义一个“头指针”,最后要调用前面所学的malloc函数动态
申请结点的方法建立整个链表。9.3.3 建立动态链表例9.12 建立学生信息链表,学生信息包括学号和成绩。学号输入为零时结束链表建
立。#include #include //调用动态
存储分配函数#define LEN sizeof(struct student) //定义符号常量LENstruct studen
t //定义结构体类型struct student{ long num; fl
oat score; struct student next;};int n;
//n为全局变量,表示结点个数(后续。。。)9.3.3 建立动态链表struct student creat(
void) //定义函数,此函数返回一个指向链表头的指针{ struct student head; //定义
指针head指向struct student类型数据 struct student p1,p2; //定义指针p1和p
2指向struct student类型数据 n=0; //结点数目为0 p1
=p2=(struct student )malloc(LEN);//用malloc函数开辟第一结点,并使p1和p2指向它
scanf("%ld,%f",&p1->num,&p1->score);//从键盘输入一个学生的信息给p1所指的第1个结点 h
ead=NULL; //使head为空,即head不指向任何结点,链表中无结点 while(p1->
num!=0) //控制p1->num不为0(约定输入的学号为0,则表示链表建立的过程完成,该结点不链接到链表中) {
n=n+1;if(n==1)head=p1; //n==1,输入的是第1个结点数据,把p1的值赋给head,即使hea
d指向新开辟的结点else p2->next=p1; // 把p1所指的结点链接到表尾(p2所指的结点)p2=p1;
//将p2移动到表尾p1=(struct student)malloc(LEN);//再开辟一个新
结点,使p1指向新结点scanf("%ld,%f",&p1->num,&p1->score);//读入新的学生数据给p1所指结点}
p2->next=NULL; //指向表尾结点的指针变量置NULL return(head);
//返回链表中第1个结点的起始地址}9.3.3 建立动态链表可以编写主函数,调用creat函数,如要输出第2个结点
成员的值int main(){ struct student pt; pt=creat();
// 函数返回链表第一个结点的地址 printf("\nnum:%ld\nscore:%5.1f\n",pt->
next->num,pt->next->score) ; //
输出第二个结点的成员值 return 0;};运行结果:1001,85.51002,921003,65.50,0num:100
2score:92返回9.3.4 结点数据的输出、删除与插入1.结点数据的输出2.结点数据的删除3.结点数据的插入9.3.4 结点
数据的输出、删除与插入1.结点数据的输出将链表中各结点的数据依次输出,首先要知道链表第一个结点的地址,也就是要知道head的地址,
然后设置一个指针变量p,指向第一个结点,输出p所指的结点,然后使p移到下一个结点并输出,直到链表的尾结点。结点数据输出操作程序为:
void print(struct student head) //定义print函数,输出struct stud
ent链表中的数据{struct student p; //在函数中定义struct student类型的变
量p printf("\nNow,These %d records are:\n",n); p=head;
//使p指向第一个结点 if(head!=NULL)
//若不是空表 do {printf("%ld %5.1f\n",
p->num,p->score //输出一个结点中的学号与成绩 p=p->next;
//p指向下一个结点 }while(p!=NULL);
//当p不是“空地址” }9.3.4 结点数据的输出、删除与插入2.结点数据的删除如图所示,在单链表L中,要删除第i个结点,只需
将第i-1个结点的指针域指向第i+1个结点,并且释放第i个结点所占的存储空间(调用free函数)。所以,删除操作程序可表示如下:v
oid Delete_L(NODE L,int i,int x) //
定义Delete_L函数,在线性链表L中,删除第i个元素 { NODE p=L,q; int j=0; w
hile( p->next && jnext;++j }; // 寻找第i个结点,并令p指
向其前驱 if(!(p->next) || j>i-1) return (0); // 表示删除位置错
误 q=p->next;p->next=q->next;free(q); // 删除并释放结点 return
(1); } 9.3.4 结点数据的输出、删除与插入3.结点数据的插入如
图9.8所示,要在链表L 中的第i-1个结点和第i个结点中插入数据元素x,首先应将指针指向结点i-1,然后生成一个新结点(调用ma
lloc函数),数据域存放x,结点i-1的指针域指向新结点x,x的指针域指向结点i。所以,插入操作程序可表示如下:9.3.4 结点
数据的输出、删除与插入void Insert_L(NODE L,int i,int x) //定义Insert_L函数,在链表L
中第i个位置之前插入元素x { NODE p=L.s; int j=0; whil
e( p && jnext;++j }; //寻找第i
-1个结点 if(!p || j>i-1) return (0); //表示插入位置错误
s=(NODE )malloc(sizeof(NODE)); //生成新结点 s->data=x;s->ne
xt=p->next;p->next=s; //将结点插入到链表中
return (1); }}【注意】以上结点输出、删除和插入的函数可单独编译,但都不能单独运行。
在具体的链表结构中,根据实际情况改变函数中的结构体类型并调用相应函数以完成输出、删除和插入的操作。9.4 用typedef声明新类
型名 C语言中,除了可使用C提供的标准类型名(如int、float)等,以及使用编写者自己构造的结构体、共用体类型外,还可以用ty
pedef指定新的类型名来替代已有的类型名。一般格式:typedef 类型名 新名称类似于定义变量,将变量名称换为新类型名,并且在
最前面加上“typedef”即可。例如:typedef int Integer //指定Integer代表int,作用
同int相同此时,“int m,n;”与“Integer m,n;”等价typedef float Real //指
定Real代表int,作用同float相同此时,“float a,b;”与“Real a,b;”等价9.4 用typedef声明新
类型名使用typedef,也可以用简单的类型名替代复杂的类型,比如替代结构体类型、共用体类型、指针类型、数组类型等。主要有以下几种
形式:1.定义一个新的类型名代表结构体类型2.定义一个新的类型名代表数组类型3.定义一个新的类型名代表指针类型4.定义一个新的类型
名代表指向函数的指针类型9.4 用typedef声明新类型名1.定义一个新的类型名代表结构体类型: 例如:typedef stru
ct //定义类型名Date表示结构体类型{ int month; int day; int year; }Da
te;Date birthday; //用Date定义结构体变量birthdayDate p;
//用Date定义结构体指针变量p,指向此结构体类型数据9.4 用typedef声明新类型名2.定义一个新的类型名代表数组类型:例
如:typedef int Arr[20]; //定义类型名Arr表示整型数组类型Arr a,b; //定义a
,b都为包含20个元素的一维数组9.4 用typedef声明新类型名3.定义一个新的类型名代表指针类型例如:typedef cha
r String; //定义类型名String表示字符指针类型String p,s[10]; //定义p为
字符指针变量,s为字符指针数组9.4 用typedef声明新类型名4.定义一个新的类型名代表指向函数的指针类型例如:typedef
int (Pointer)(); //定义类型名Pointer表示指向函数的指针类型Pointer p1,p2;
//定义P1,p2为Pointer类型的指针变量【说明】① 用typedef可以定义各种类型名,但不能直接用来定义变量。② 用ty
pedef只是对已存在的类型增加一个新的类型名称,并不构造新类型。③ 在不同源文件中使用同一类型数据,常使用typedef说明这些
数据类型,并单独存放在一个文件中,在需要时,用#include命令包含进来。9.4 用typedef声明新类型名例9.13:若有以
下语句 (2010年9月全国计算机等级考试二级C试题选择题第39题)typedef struct S  {int g; char
h;}T;以下叙述中正确的是A) 可用S定义结构体变量 B) 可用T定义结构体变量C) S是struct类型的变量 D) T是
struct S类型的变量分析:使用typedef定义新类型,是采用类似定义变量的方式那样先生成一个类型名,再用它去定义变量。此题
中,T是替代struct S的类型名,应用T去定义变量,所以选B。如果没有使用typedef,则T代表的是struct S类型的变
量,S代表的是结构体类型名,可用S定义结构体变量。9.4 用typedef声明新类型名例9.14 以下程序把三个NODETYPE型
的变量链接成一个简单的链表,并在while循环中输出链表结点数据域中的数据。请填空。(2009年3月全国计算机等级考试二级C试题填
空第15题)#include struct node //定义结构体类型struct
node{ int data; struct node next; //定义next为指针变量,指向结构体变量
};typedef struct node NODETYPE; //用typedef定义新的类型名NODETYPE表示结构体m
ain(){NODETYPE a,b,c, h,p; //定义3个结构体变量a,b,c作为链表的结点a.data=
10; b.data=20; c.data=30; //对结点a、b、c的成员data赋值h=&a;
//将结点a的起始地址赋给头指针ha.next=&b; //将结点b的起始地址赋给a结点的next成员b.next=&c; //将结点c的起始地址赋给b结点的next成员c.next=''\0''; //c结点的next成员置为NULL p=h; //使p指向a结点while (p) //当p不为空时{ printf("%d,", p->data); _____;}printf ("\n");}9.4 用typedef声明新类型名例9.14分析:此题结合了静态链表和使用typedef的相关知识点。静态链表中所有结点都已定义,“printf(“%d”,p->data)“表示输出p指向的结点数据,要将后面的数据输出,应使p指向下一结点,所以用p=p->next使p指向下一个结点。答案为p=p->next9.4 用typedef声明新类型名例9.15 有以下程序(2011年9月全国计算机等级考试二级C试题选择题第36题)#include #include typedef struct{char name[9];char sex;int score[2];}STU; //用typedef定义结构体类型名称STU f(STU a) //定义f函数,功能是将结构体变量b的所有成员值赋给结构体变量a的成员{STU b={"Zhao",''m'',85,90}; int i; strcpy(a.name,b.name); a.sex=b.sex; for(i=0;i<2;i++) a.score[i]=b.score[i]; return a;}main(){STU c={"Qian",''f'',95,92},d;//定义结构体变量c并初始化,定义结构体变量dd=f(c); // 调用f函数printf("%s,%c,%d,%d,",d.name,d.sex,d.score[0],d.score[1]);printf("%s,%c,%d,%d\n",c.name,c.sex,c.score[0],c.score[1]);}9.4 用typedef声明新类型名例9.15程序运行后的输出结果是A) zhao,m,85,90,Qian,f,95,92B) zhao,m,85,90,zhao,m,85,90 C) Qian,f,95,92,Qian,f,95,92 D) Qian,f,95,92,zhao,m,85,90 分析:本题考查了结构体的知识和函数调用的知识。主函数里面,通过d=f(c)调用f函数,将实参c的所有数据传递给f函数的形参a。f函数执行的操作是将b中的数据全部复制到a中。所以返回a的给d后,d的值变成了zhao,m,85,90。函数调用过程中,结构体变量c的值不发生改变。所以选A。
献花(0)
+1
(本文系小磊老师首藏)