第 8章 结构体与联合体
8.1 程序与程序文件
8.2 结构体数组
8.3 结构体与指针
8.4 链 表
8.5 联 合 体
8.6 枚举类型与自定义类型名
8.7 程序举例
8.1 程序与程序文件
8.1.1 结构体类型变量的定义
定义结构类型变量包括两个方面:首先要定义结构体
类型, 以便确定该类型中有哪些成员, 各成员属于什么数
据类型;然后再定义属于该结构体类型的变量 。
1.定义结构体类型
定义结构体类型的一般形式如下:
struct 结构体类型名
{ 成员表 };
其中在“成员表”中定义了该类型中有哪些成员,
各成员属于什么数据类型。
2,定义结构体类型变量
定义结构体类型变量的一般形式为
struct 结构体类型名 变量表;
定义结构体类型与定义结构体类型变量是分开说明
的 。 C语言还允许在定义结构体类型的同时定义结构体
类型变量 。 其形式为
struct 结构体类型名
{ 成员表 } 变量表;
如果在函数体外定义了一个结构体类型, 则从
定义位置开始到整个程序文件结束之间的所有函数
中均可定义该类型的变量;但在函数体内所定义的
结构体类型, 只能在该函数体内能定义该类型的变
量 。 即结构体类型的定义与普通变量定义的作用域
是相同的 。
8.1.2 结构体类型变量的引用
在程序中定义了某结构体类型的变量后就可以被引用 。
结构体变量的一般引用方式如下:
结构体变量名,成员名
其中,,”为结构体成员运算符,它的优先级最高。
8.1.3 结构体的嵌套
C语言规定,结构体类型的定义可以嵌套。
8.1.4 结构体类型变量的初始化
与普通变量一样, 在定义结构体类型变量的同时也
可以对结构体类型变量赋初值 。 但 C语言规定, 只能对
全局的或静态的局部结构体类型变量进行初始化 。 为了
将结构体类型变量定义为静态存储类型, 在定义时应加
上 static关键字 。 但是, 目前在大部分计算机系统中, 对
结构体类型变量初始化时不必加 static关键字, 其原理与
普通数组的初始化一样 。
8.1.5 结构体与函数
1.结构体类型变量的成员作为函数参数
与数组元素可以作为函数参数一样,结构体类型
变量中的成员也可以作为函数参数。在这种情况下,
在被调用函数中的形参是一般变量,而调用函数中的
实参是结构体类型变量中的一个成员,但要求它们的
类型应一致。
2,结构体类型变量作为函数参数
与一般变量可以作为函数参数一样, 结构体类型的变量
也可以作为函数参数 。 在这种情况下, 在被调用函数中的形
参是结构体类型的变量, 调用函数中的实参也是结构体类型
的变量, 但要求它们属于同一个结构体类型 。
3.结构体类型的函数
与定义标准数据类型函数一样,C语言也允许
定义结构体类型的函数。结构体类型函数的返回值
是结构体类型的数据。
8.2 结构体数组
8.2.1 结构体数组的定义与引用
与整型数组、实型数组、字符型数组一样,在
程序中也可以定义结构体类型的数组。但 C语言规
定,同一个结构体数组中的元素应为同一种结构
体类型。
例如,
struct student
{ int num;
char name[10];
char sex;
int age;
float score[3];
} stu[10];
定义了, 学生情况, 型的一个数组 stu,可存放 10个学
生的情况 。 每一个学生的情况包括:学号 (num),姓名
(name[10]),性别 (sex),年龄 (age),3个成绩 (score[3])。
实际上, 定义了该数组后, 相当于开辟了一个如表 8.1所
示的表格空间 。
表 8.1 学生情况型的数组表格空间
num
学 号
name
姓 名
sex
性 别
age
年 龄
score[0]
成绩 1
score[1]
成绩 2
score[2]
成绩 3
8.2.2 结构体数组作为函数参数
与普通数组一样, 结构体类型数组也能作为函数参
数, 并且形参与实参结合的方式完全一样 。 如果在被
调用函数中改变了结构体类型形参数组元素中各成员
值, 实际上也就改变了结构体类型实参数组元素中的
各成员值 。 因为结构体类型形参数组与结构体类型实
参数组是同一个存储空间 。
8.3 结构体与指针
8.3.1 结构体类型指针变量的定义与引用
结构体类型的指针变量指向结构体类型变量或数组 (或数
组元素 )的起始地址 。
例如,
struct student
{ int num;
char name[10];
char sex;
int age;
float score;
};
struct student st1,st2,st[10],*p;
其中定义了一个指向结构体, 学生情况, 型的指针 p。
由上所述,当结构体类型的指针变量 p指向
一个结构体类型变量后,下列 3种表示是等价的:
结构体变量名,成员
(*p).成员
p- >成员
它们都表示结构体变量中的一个成员。
8.3.2 结构体类型指针作为函数参数
结构体类型指针可以指向结构体类型的变量,
因此,当形参是结构体类型指针变量时,实参也可
以是结构体类型指针(即地址)。在结构体类型指
针作为函数参数的情况下,由于传送的是地址,因
此,如果在被调用函数中改变了结构体类型形参指
针所指向的地址中的值,实际上也就改变了结构体
类型实参指针所指向的地址中的值。
例 8.7 用结构体类型指针作为函数参数 。
在下面的程序中, 主函数的功能是定义了一个结构体
student型的变量 st,同时为之初始化, 然后输出变量 st中各
成员的值, 将结构体类型变量 st的地址 ( 即 &st) 作为实参调
用函数 chang()后再输出变量 st中各成员的值;函数 chang()的
功能是修改结构体类型形参指针 t所指向的结构体类型数据中
成员 t- >score的值, 并输出修改前后结构体类型指针所指向
的数据中各成员的值 。
#include "stdio.h"
struct student
{ int num;
char name[10];
char sex;
int age;
float score;
};
void chang(t)
struct student *t;
{ printf("t= %6d%8s%3c%4d%7.2f\n",
t- >num,t- >name,t- >sex,t- >age,t- >score);
t- >score= 95.0;
printf("t= %6d%8s%3c%4d%7.2f\n",
t- >num,t- >name,t- >sex,t- >age,t- >score);
}
main()
{ static struct student st= {101,"Zhang",'M',19,89.0};
printf("st= %6d%8s%3c%4d%7.2f\n",
st.num,st.name,st.sex,st.age,st.score);
chang(&st);
printf("st= %6d%8s%3c%4d%7.2f\n",
st.num,st.name,st.sex,st.age,st.score);
}
结构体类型指针也可以指向数组或数组元素,因此,
当形参是结构体类型指针变量时,实参也可以是结构体类
型数组名或数组元素的地址。
与标准数据类型的数组与指针一样, 在结构体类型数组
指针作函数参数时, 也可以有以下 4种情况:
( 1) 实参与形参都用结构体类型数组名;
( 2) 实参用结构体类型数组名, 形参用结构体类型指针变
量;
( 3) 实参与形参都用结构体类型指针变量;
( 4) 实参用结构体类型指针变量, 形参用结构体类型数组
名 。
8.4 链 表
8.4.1 链表的基本概念
1,链表的一般结构
链表由结点元素组成 。 为了适应链表的存储结构, 计
算机存储空间被划分为一个一个小块, 每一小块占若干字
节, 通常称这些小块为存储结点 。
将存储空间中的每一个存储结点分为两部分:一部分
用于存储数据元素的值, 称为数据域;另一部分用于存放
下一个数据元素的存储序号 ( 即存储结点的地址 ), 称为
指针域 。
数据域
指针域
存放数据元素
存放下一个结点元素的地址
每一个结点的结构如图 8.1所示。
图 8.1 链表的结点结构
在链表中, 用一个专门的指针 HEAD指向链表中第一
个数据元素的结点 ( 即存放第一个数据元素的存储结点的
序号 ) 。 链表中最后一个元素后面已没有结点元素, 因此,
链表中最后一个结点的指针域为空 ( 用 NULL或 0表示 ),
表示链表终止 。 链表的逻辑结构如图 8.2所示 。
数据 1 H E A D 数据 2 数据 n N U L L ?
图 8.2 链表的逻辑结构
2,结点结构体类型的定义
在 C语言中, 定义链表结点结构的一般形式如下:
struct 结构体名
{ 数据成员表;
struct 结构体名 *指针变量名;
};
3,结点的动态分配
在 C语言中, 可以利用 malloc 函数向系统申请分配链
表结点的存储空间, 其形式为
malloc(存储区字节数 )
该函数返回存储区的首地址 。 例如,
struct node
{ int d;
struct node *next;
};
struct node *p;
p= (struct node * )malloc(sizeof(struct node));
释放存储区用如下函数:
free(p);
8.4.2 链表的基本运算
1,在链表中查找指定元素
在对链表进行插入或删除的运算中, 总是首先需要找
到插入或删除的位置, 这就需要对链表进行扫描查找, 在
链表中寻找包含指定元素值的前一个结点 。 当找到包含指
定元素的前一个结点后, 就可以在该结点后插入新结点或
删除该结点后的一个结点 。
下面是在非空链表中寻找包含指定元素值的前一个结点的 C
语言描述 。
struct node /*定义结点类型 */
{ ET d; /*ET为数据元素类型名, 下同 */
struct node *next;
};
/*在头指针为 head的非空链表中寻找包含元素 x的前一个结点 p( 结点 p作为
函数值返回 ) */
struct node *lookst(head,x)
ET x; struct node *head;
{ struct node *p;
p= head;
while((p- >next!= NULL)&&(((p- >next)- >d)!= x)) p= p- >next;
return(p);
}
2,链表的插入
链表的插入是指在原链表中的指定元素之前插入一个新元
素 。
要在链表中包含元素 x的结点之前插入一个新元素 b。 其
插入过程如下:
( 1) 用 malloc()函数申请取得新结点 p,并置该结点的数据域
为 b。 即令 p- >d= b。
( 2) 在链表中寻找包含元素 x的前一个结点, 设该结点的存
储地址为 q。 链表如图 8.3( b) 所示 。
( 3) 最后将结点 p插入到结点 q之后 。 为了实现这一步, 只要
改变以下两个结点的指针域内容:
① 使结点 p指向包含元素 x的结点(即结点 q的后件
结点),即令
p- >next= q- >next
② 使结点 q的指针域内容改为指向结点 p,即令
q- >next= p
图 8.3 链表的插入
x HEA D 0 ? ?
(a) 原来的链表
x HEA D 0 ? ?
(b ) 申请得到结点 p,在链表中找到包含元素 x 的前一个结点 q
b
p
q
x HEA D 0 ? ?
(c) p 插入到 q 之后
b
p
q
3,链表的删除
链表的删除是指在链表中删除包含指定元素的结点 。
为了在链表中删除包含指定元素的结点, 首先要在链表中找
到这个结点, 然后将要删除结点放回到可利用栈 。
要在链表中删除包含元素 x的结点 。 其删除过程如下:
( 1) 在链表中寻找包含元素 x的前一个结点, 设该结点地址
为 q。 则包含元素 x的结点地址 p= q- >next。
( 2) 将结点 q后的结点 p从链表中删除, 即让结点 q的指针指
向包含元素 x的结点 p的指针指向的结点, 即令
q- >next= p- >next
( 3) 将包含元素 x的结点 p释放 。 此时, 链表的删除运算完
成 。
图 8.4 链表的删除
x H EA D 0 ? ?
( a ) 原来的链表
x H EA D 0 ? ?
( b) 从链表中删除包含元素 x 的结点 p 后
q p
8.5 联 合 体
C语言中中的联合数据类型可以满足这种需要 。 联
合体又称为共用体, 意为各种不同数据共用同一段存储
空间 。
与结构体类似, 为了定义联合体类型变量, 首先要
定义联合体类型, 说明该联合体类型中包括哪些成员,
它们各属于何数据类型, 然后再定义该类型的变量 。
定义联合体数据类型的一般形式为
union 联合体名
{ 成员表 };
例如,
union w
{ int k; double d; char c; };
定义了一个联合体类型 w,包括代表整型量的成员 k、代表
双精度型量的成员 d和代表字符型量的成员 c。
下面对联合体类型变量作几点说明:
( 1) 由于一个联合体变量中的各成员共用一段存储空
间, 因此, 在任一时刻, 只能有一种类型的数据存放在该
变量中, 即在任一时刻, 只有一个成员的数据有意义, 其
他成员的数据是没有意义的 。
( 2)在引用联合体变量中的成员时,必须保证数据的
一致。
( 3) 在定义联合体变量时不能为其初始化, 并且, 联
合体变量不能作为函数参数 。
( 4) 联合体类型与结构体类型可以互相嵌套, 即联合
体类型可以作为结构体类型的成员, 结构体类型也可以作
为联合体类型的成员 。
8.6 枚举类型与自定义类型名
8.6.1 枚举类型
( 1) 先定义枚举类型, 然后定义该枚举类型的变量 。
定义枚举类型的一般形式为
enum 枚举类型名 { 枚举元素列表 };
其中在枚举元素列表中依次列出了该类型中所有的元素
( 即枚举常量 ), 如果在定义中没有显式地给出这些元素的
值, 这些元素依次取值为 0,1,2,… 。
( 2) 在定义枚举类型的同时定义该枚举类型的变量 。
这种定义方法的一般形式为
enum 枚举类型名 { 枚举元素列表 }变量表;
( 3) 直接定义枚举类型变量 。
这种定义方法的一般形式为
enum { 枚举元素列表 }变量表;
在使用枚举类型数据时, 要注意以下几个问题:
( 1)不能对枚举元素赋值,因为枚举元素本身就是常量(
即枚举常量)。
( 2)虽然在程序中不能对枚举元素赋值,但实际上,每个
枚举元素都有一个确定的整型值。
( 3) C语言允许将一个整型值经强制类型转换后赋给枚举
类型变量。
8.6.2 自定义类型名
在一个 C程序中, 可以使用 C提供的标准数据类型名
( 如 int,char,float,double等 ), 也可以使用用户自
己定义的数据类型名 ( 如结构体类型, 联合体类型, 枚
举类型等 ) 。 除此之外, C语言还允许用 typedef声明新
的类型名来代表已有的类型名, 称为自定义类型名 。
自定义类型名的一般形式为
typedef 原类型名 新类型名;
它指定用新类型名代表原类型名 。
8.7 程序举例
例 8.11 设有学生情况登记表如表 8.3所示 。 用选择排序法对该表
按成绩从小到大进行排序 。
表 8.3 学生情况登记表
学号 num 姓名 name[8] 性别 sex 年龄 age 成绩 score
101 Zhang M 19 95.6
102 Wang F 18 92.4
103 Zhao M 19 85.7
104 Li M 20 96.3
105 Gou M 19 90.2
106 Lin M 18 91.5
107 Ma F 17 98.7
108 Zhen M 21 90.1
109 Xu M 19 89.8
110 Mao F 18 94.9
其 C程序如下:
#define STUDENT struct student
STUDENT
{ int num;
char name[8];
char sex;
int age;
double score;
};
#include "stdio.h"
main()
{ int i;
void sort();
static STUDENT stu[10]= {{101,"Zhang",'M',19,95.6},
{102,"Wang",'F',18,92.4},
{103,"Zhao",'M',19,85.7},
{104,"Li",'M',20,96.3},
{105,"Gou",'M',19,90.2},
{106,"Lin",'M',18,91.5},
{107,"Ma",'F',17,98.7},
{108,"Zhen",'M',21,90.1},
{109,"Xu",'M',19,89.8},
{110,"Mao",'F',18,94.9}};
STUDENT *p[10];
for (i= 0; i<= 9; i++ ) p[i]= &stu[i];
printf("\n");
printf("No,Name Sex Age Score\n");
for (i= 0; i<= 9; i++ )
printf("%- 8d%- 9s%- 8c%- 8d%- 5.2f\n",
(*p[i]).num,(*p[i]).name,(*p[i]).sex,
(*p[i]).age,(*p[i]).score);
printf("\n");
sort(p,10);
printf("No,Name Sex Age Score\n");
for (i= 0; i<= 9; i++ )
printf("%- 8d%- 9s%- 8c%- 8d%- 5.2f\n",
(*p[i]).num,(*p[i]).name,(*p[i]).sex,
(*p[i]).age,(*p[i]).score);
printf("\n");
}
void sort(p,n)
int n;
STUDENT *p[];
{ int i,j,k;
STUDENT *w;
for (i= 0; i< n- 1; i++ )
{ k= i;
for (j= i+ 1; j< n; j++ )
if ((*p[j]).score< (*p[k]).score) k= j;
if (k!= i)
{ w= p[i]; p[i]= p[k]; p[k]= w; }
}
return;
}
8.1 程序与程序文件
8.2 结构体数组
8.3 结构体与指针
8.4 链 表
8.5 联 合 体
8.6 枚举类型与自定义类型名
8.7 程序举例
8.1 程序与程序文件
8.1.1 结构体类型变量的定义
定义结构类型变量包括两个方面:首先要定义结构体
类型, 以便确定该类型中有哪些成员, 各成员属于什么数
据类型;然后再定义属于该结构体类型的变量 。
1.定义结构体类型
定义结构体类型的一般形式如下:
struct 结构体类型名
{ 成员表 };
其中在“成员表”中定义了该类型中有哪些成员,
各成员属于什么数据类型。
2,定义结构体类型变量
定义结构体类型变量的一般形式为
struct 结构体类型名 变量表;
定义结构体类型与定义结构体类型变量是分开说明
的 。 C语言还允许在定义结构体类型的同时定义结构体
类型变量 。 其形式为
struct 结构体类型名
{ 成员表 } 变量表;
如果在函数体外定义了一个结构体类型, 则从
定义位置开始到整个程序文件结束之间的所有函数
中均可定义该类型的变量;但在函数体内所定义的
结构体类型, 只能在该函数体内能定义该类型的变
量 。 即结构体类型的定义与普通变量定义的作用域
是相同的 。
8.1.2 结构体类型变量的引用
在程序中定义了某结构体类型的变量后就可以被引用 。
结构体变量的一般引用方式如下:
结构体变量名,成员名
其中,,”为结构体成员运算符,它的优先级最高。
8.1.3 结构体的嵌套
C语言规定,结构体类型的定义可以嵌套。
8.1.4 结构体类型变量的初始化
与普通变量一样, 在定义结构体类型变量的同时也
可以对结构体类型变量赋初值 。 但 C语言规定, 只能对
全局的或静态的局部结构体类型变量进行初始化 。 为了
将结构体类型变量定义为静态存储类型, 在定义时应加
上 static关键字 。 但是, 目前在大部分计算机系统中, 对
结构体类型变量初始化时不必加 static关键字, 其原理与
普通数组的初始化一样 。
8.1.5 结构体与函数
1.结构体类型变量的成员作为函数参数
与数组元素可以作为函数参数一样,结构体类型
变量中的成员也可以作为函数参数。在这种情况下,
在被调用函数中的形参是一般变量,而调用函数中的
实参是结构体类型变量中的一个成员,但要求它们的
类型应一致。
2,结构体类型变量作为函数参数
与一般变量可以作为函数参数一样, 结构体类型的变量
也可以作为函数参数 。 在这种情况下, 在被调用函数中的形
参是结构体类型的变量, 调用函数中的实参也是结构体类型
的变量, 但要求它们属于同一个结构体类型 。
3.结构体类型的函数
与定义标准数据类型函数一样,C语言也允许
定义结构体类型的函数。结构体类型函数的返回值
是结构体类型的数据。
8.2 结构体数组
8.2.1 结构体数组的定义与引用
与整型数组、实型数组、字符型数组一样,在
程序中也可以定义结构体类型的数组。但 C语言规
定,同一个结构体数组中的元素应为同一种结构
体类型。
例如,
struct student
{ int num;
char name[10];
char sex;
int age;
float score[3];
} stu[10];
定义了, 学生情况, 型的一个数组 stu,可存放 10个学
生的情况 。 每一个学生的情况包括:学号 (num),姓名
(name[10]),性别 (sex),年龄 (age),3个成绩 (score[3])。
实际上, 定义了该数组后, 相当于开辟了一个如表 8.1所
示的表格空间 。
表 8.1 学生情况型的数组表格空间
num
学 号
name
姓 名
sex
性 别
age
年 龄
score[0]
成绩 1
score[1]
成绩 2
score[2]
成绩 3
8.2.2 结构体数组作为函数参数
与普通数组一样, 结构体类型数组也能作为函数参
数, 并且形参与实参结合的方式完全一样 。 如果在被
调用函数中改变了结构体类型形参数组元素中各成员
值, 实际上也就改变了结构体类型实参数组元素中的
各成员值 。 因为结构体类型形参数组与结构体类型实
参数组是同一个存储空间 。
8.3 结构体与指针
8.3.1 结构体类型指针变量的定义与引用
结构体类型的指针变量指向结构体类型变量或数组 (或数
组元素 )的起始地址 。
例如,
struct student
{ int num;
char name[10];
char sex;
int age;
float score;
};
struct student st1,st2,st[10],*p;
其中定义了一个指向结构体, 学生情况, 型的指针 p。
由上所述,当结构体类型的指针变量 p指向
一个结构体类型变量后,下列 3种表示是等价的:
结构体变量名,成员
(*p).成员
p- >成员
它们都表示结构体变量中的一个成员。
8.3.2 结构体类型指针作为函数参数
结构体类型指针可以指向结构体类型的变量,
因此,当形参是结构体类型指针变量时,实参也可
以是结构体类型指针(即地址)。在结构体类型指
针作为函数参数的情况下,由于传送的是地址,因
此,如果在被调用函数中改变了结构体类型形参指
针所指向的地址中的值,实际上也就改变了结构体
类型实参指针所指向的地址中的值。
例 8.7 用结构体类型指针作为函数参数 。
在下面的程序中, 主函数的功能是定义了一个结构体
student型的变量 st,同时为之初始化, 然后输出变量 st中各
成员的值, 将结构体类型变量 st的地址 ( 即 &st) 作为实参调
用函数 chang()后再输出变量 st中各成员的值;函数 chang()的
功能是修改结构体类型形参指针 t所指向的结构体类型数据中
成员 t- >score的值, 并输出修改前后结构体类型指针所指向
的数据中各成员的值 。
#include "stdio.h"
struct student
{ int num;
char name[10];
char sex;
int age;
float score;
};
void chang(t)
struct student *t;
{ printf("t= %6d%8s%3c%4d%7.2f\n",
t- >num,t- >name,t- >sex,t- >age,t- >score);
t- >score= 95.0;
printf("t= %6d%8s%3c%4d%7.2f\n",
t- >num,t- >name,t- >sex,t- >age,t- >score);
}
main()
{ static struct student st= {101,"Zhang",'M',19,89.0};
printf("st= %6d%8s%3c%4d%7.2f\n",
st.num,st.name,st.sex,st.age,st.score);
chang(&st);
printf("st= %6d%8s%3c%4d%7.2f\n",
st.num,st.name,st.sex,st.age,st.score);
}
结构体类型指针也可以指向数组或数组元素,因此,
当形参是结构体类型指针变量时,实参也可以是结构体类
型数组名或数组元素的地址。
与标准数据类型的数组与指针一样, 在结构体类型数组
指针作函数参数时, 也可以有以下 4种情况:
( 1) 实参与形参都用结构体类型数组名;
( 2) 实参用结构体类型数组名, 形参用结构体类型指针变
量;
( 3) 实参与形参都用结构体类型指针变量;
( 4) 实参用结构体类型指针变量, 形参用结构体类型数组
名 。
8.4 链 表
8.4.1 链表的基本概念
1,链表的一般结构
链表由结点元素组成 。 为了适应链表的存储结构, 计
算机存储空间被划分为一个一个小块, 每一小块占若干字
节, 通常称这些小块为存储结点 。
将存储空间中的每一个存储结点分为两部分:一部分
用于存储数据元素的值, 称为数据域;另一部分用于存放
下一个数据元素的存储序号 ( 即存储结点的地址 ), 称为
指针域 。
数据域
指针域
存放数据元素
存放下一个结点元素的地址
每一个结点的结构如图 8.1所示。
图 8.1 链表的结点结构
在链表中, 用一个专门的指针 HEAD指向链表中第一
个数据元素的结点 ( 即存放第一个数据元素的存储结点的
序号 ) 。 链表中最后一个元素后面已没有结点元素, 因此,
链表中最后一个结点的指针域为空 ( 用 NULL或 0表示 ),
表示链表终止 。 链表的逻辑结构如图 8.2所示 。
数据 1 H E A D 数据 2 数据 n N U L L ?
图 8.2 链表的逻辑结构
2,结点结构体类型的定义
在 C语言中, 定义链表结点结构的一般形式如下:
struct 结构体名
{ 数据成员表;
struct 结构体名 *指针变量名;
};
3,结点的动态分配
在 C语言中, 可以利用 malloc 函数向系统申请分配链
表结点的存储空间, 其形式为
malloc(存储区字节数 )
该函数返回存储区的首地址 。 例如,
struct node
{ int d;
struct node *next;
};
struct node *p;
p= (struct node * )malloc(sizeof(struct node));
释放存储区用如下函数:
free(p);
8.4.2 链表的基本运算
1,在链表中查找指定元素
在对链表进行插入或删除的运算中, 总是首先需要找
到插入或删除的位置, 这就需要对链表进行扫描查找, 在
链表中寻找包含指定元素值的前一个结点 。 当找到包含指
定元素的前一个结点后, 就可以在该结点后插入新结点或
删除该结点后的一个结点 。
下面是在非空链表中寻找包含指定元素值的前一个结点的 C
语言描述 。
struct node /*定义结点类型 */
{ ET d; /*ET为数据元素类型名, 下同 */
struct node *next;
};
/*在头指针为 head的非空链表中寻找包含元素 x的前一个结点 p( 结点 p作为
函数值返回 ) */
struct node *lookst(head,x)
ET x; struct node *head;
{ struct node *p;
p= head;
while((p- >next!= NULL)&&(((p- >next)- >d)!= x)) p= p- >next;
return(p);
}
2,链表的插入
链表的插入是指在原链表中的指定元素之前插入一个新元
素 。
要在链表中包含元素 x的结点之前插入一个新元素 b。 其
插入过程如下:
( 1) 用 malloc()函数申请取得新结点 p,并置该结点的数据域
为 b。 即令 p- >d= b。
( 2) 在链表中寻找包含元素 x的前一个结点, 设该结点的存
储地址为 q。 链表如图 8.3( b) 所示 。
( 3) 最后将结点 p插入到结点 q之后 。 为了实现这一步, 只要
改变以下两个结点的指针域内容:
① 使结点 p指向包含元素 x的结点(即结点 q的后件
结点),即令
p- >next= q- >next
② 使结点 q的指针域内容改为指向结点 p,即令
q- >next= p
图 8.3 链表的插入
x HEA D 0 ? ?
(a) 原来的链表
x HEA D 0 ? ?
(b ) 申请得到结点 p,在链表中找到包含元素 x 的前一个结点 q
b
p
q
x HEA D 0 ? ?
(c) p 插入到 q 之后
b
p
q
3,链表的删除
链表的删除是指在链表中删除包含指定元素的结点 。
为了在链表中删除包含指定元素的结点, 首先要在链表中找
到这个结点, 然后将要删除结点放回到可利用栈 。
要在链表中删除包含元素 x的结点 。 其删除过程如下:
( 1) 在链表中寻找包含元素 x的前一个结点, 设该结点地址
为 q。 则包含元素 x的结点地址 p= q- >next。
( 2) 将结点 q后的结点 p从链表中删除, 即让结点 q的指针指
向包含元素 x的结点 p的指针指向的结点, 即令
q- >next= p- >next
( 3) 将包含元素 x的结点 p释放 。 此时, 链表的删除运算完
成 。
图 8.4 链表的删除
x H EA D 0 ? ?
( a ) 原来的链表
x H EA D 0 ? ?
( b) 从链表中删除包含元素 x 的结点 p 后
q p
8.5 联 合 体
C语言中中的联合数据类型可以满足这种需要 。 联
合体又称为共用体, 意为各种不同数据共用同一段存储
空间 。
与结构体类似, 为了定义联合体类型变量, 首先要
定义联合体类型, 说明该联合体类型中包括哪些成员,
它们各属于何数据类型, 然后再定义该类型的变量 。
定义联合体数据类型的一般形式为
union 联合体名
{ 成员表 };
例如,
union w
{ int k; double d; char c; };
定义了一个联合体类型 w,包括代表整型量的成员 k、代表
双精度型量的成员 d和代表字符型量的成员 c。
下面对联合体类型变量作几点说明:
( 1) 由于一个联合体变量中的各成员共用一段存储空
间, 因此, 在任一时刻, 只能有一种类型的数据存放在该
变量中, 即在任一时刻, 只有一个成员的数据有意义, 其
他成员的数据是没有意义的 。
( 2)在引用联合体变量中的成员时,必须保证数据的
一致。
( 3) 在定义联合体变量时不能为其初始化, 并且, 联
合体变量不能作为函数参数 。
( 4) 联合体类型与结构体类型可以互相嵌套, 即联合
体类型可以作为结构体类型的成员, 结构体类型也可以作
为联合体类型的成员 。
8.6 枚举类型与自定义类型名
8.6.1 枚举类型
( 1) 先定义枚举类型, 然后定义该枚举类型的变量 。
定义枚举类型的一般形式为
enum 枚举类型名 { 枚举元素列表 };
其中在枚举元素列表中依次列出了该类型中所有的元素
( 即枚举常量 ), 如果在定义中没有显式地给出这些元素的
值, 这些元素依次取值为 0,1,2,… 。
( 2) 在定义枚举类型的同时定义该枚举类型的变量 。
这种定义方法的一般形式为
enum 枚举类型名 { 枚举元素列表 }变量表;
( 3) 直接定义枚举类型变量 。
这种定义方法的一般形式为
enum { 枚举元素列表 }变量表;
在使用枚举类型数据时, 要注意以下几个问题:
( 1)不能对枚举元素赋值,因为枚举元素本身就是常量(
即枚举常量)。
( 2)虽然在程序中不能对枚举元素赋值,但实际上,每个
枚举元素都有一个确定的整型值。
( 3) C语言允许将一个整型值经强制类型转换后赋给枚举
类型变量。
8.6.2 自定义类型名
在一个 C程序中, 可以使用 C提供的标准数据类型名
( 如 int,char,float,double等 ), 也可以使用用户自
己定义的数据类型名 ( 如结构体类型, 联合体类型, 枚
举类型等 ) 。 除此之外, C语言还允许用 typedef声明新
的类型名来代表已有的类型名, 称为自定义类型名 。
自定义类型名的一般形式为
typedef 原类型名 新类型名;
它指定用新类型名代表原类型名 。
8.7 程序举例
例 8.11 设有学生情况登记表如表 8.3所示 。 用选择排序法对该表
按成绩从小到大进行排序 。
表 8.3 学生情况登记表
学号 num 姓名 name[8] 性别 sex 年龄 age 成绩 score
101 Zhang M 19 95.6
102 Wang F 18 92.4
103 Zhao M 19 85.7
104 Li M 20 96.3
105 Gou M 19 90.2
106 Lin M 18 91.5
107 Ma F 17 98.7
108 Zhen M 21 90.1
109 Xu M 19 89.8
110 Mao F 18 94.9
其 C程序如下:
#define STUDENT struct student
STUDENT
{ int num;
char name[8];
char sex;
int age;
double score;
};
#include "stdio.h"
main()
{ int i;
void sort();
static STUDENT stu[10]= {{101,"Zhang",'M',19,95.6},
{102,"Wang",'F',18,92.4},
{103,"Zhao",'M',19,85.7},
{104,"Li",'M',20,96.3},
{105,"Gou",'M',19,90.2},
{106,"Lin",'M',18,91.5},
{107,"Ma",'F',17,98.7},
{108,"Zhen",'M',21,90.1},
{109,"Xu",'M',19,89.8},
{110,"Mao",'F',18,94.9}};
STUDENT *p[10];
for (i= 0; i<= 9; i++ ) p[i]= &stu[i];
printf("\n");
printf("No,Name Sex Age Score\n");
for (i= 0; i<= 9; i++ )
printf("%- 8d%- 9s%- 8c%- 8d%- 5.2f\n",
(*p[i]).num,(*p[i]).name,(*p[i]).sex,
(*p[i]).age,(*p[i]).score);
printf("\n");
sort(p,10);
printf("No,Name Sex Age Score\n");
for (i= 0; i<= 9; i++ )
printf("%- 8d%- 9s%- 8c%- 8d%- 5.2f\n",
(*p[i]).num,(*p[i]).name,(*p[i]).sex,
(*p[i]).age,(*p[i]).score);
printf("\n");
}
void sort(p,n)
int n;
STUDENT *p[];
{ int i,j,k;
STUDENT *w;
for (i= 0; i< n- 1; i++ )
{ k= i;
for (j= i+ 1; j< n; j++ )
if ((*p[j]).score< (*p[k]).score) k= j;
if (k!= i)
{ w= p[i]; p[i]= p[k]; p[k]= w; }
}
return;
}