第八章 结构体与共用体
枚举类型
河北建筑工程学院
2004年 3月
迄今为止, 我们已详细介绍了基本数据类型如整
型, 实型, 字符型等, 也介绍了一种构造类型的数
据 —数组, 数组中的各元素是属于同一个类型的 。
但在处理实际问题时, 经常会遇到复杂的数据, 只
有这些数据类型是不够的, 还需要将不同类型的数
据组合成一个有机的整体, 以便于引用 。 为了能把
这些有一定逻辑联系的数据组成一个整体, C++语
言提供了一种结构体数据类型本章主要介绍由不同
类型数据组成的构造类型的数据, 包括结构体类型
共用体类型和枚举类
8.1 结构体类型
?用途:把 不同类型 的数据组合成一个整体 自定
义 数据类型
?结构体是 一种 构造 数据类型
?例如:在描述学生的基本情况时,一般要用到
学生的学号、姓名、性别、年龄、成绩、家庭
住址等项,这些项都与某一学生相联系,见图
8.1。
100001 王彤 男 21 90 北京
学号 姓名 性别 年龄 成绩 地址
图 8.1 学生的基本情况
可以看到学号 ( num), 姓名 (name),性
别 (sex),年龄 (age),成绩 (score),家庭住址
(addr)共同描述名为, 王彤, 的学生, 如果将
num,name,sex,age,score,addr分别定
义为互相独立的简单变量, 则难以反映它们之间
的内在联系 。 应当把它们组织成一个组合项, 在
一个组合项中包含若干个类型相同或不同的数据
项 。 C++语言允许用户指定这样一种数据结构,
称为结构体 ( structure) 它相当于其它高级语
言中的, 记录,
使用结构体类型之前, 必须先对结构体的组
成进行描述这就是结构体类型的定义 。 结构体类
型的定义描述了组成结构体的成员以及每个成员
的数据类型 。
结构体类型定义
struct [结构体名 ]
{
类型标识符 成员名;
类型标识符 成员名;
……………,
};
成员类型可以是
基本型或构造型 struct是 关键字,
不能省略
合法标识符
可省,无名结构体
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
结构体类型定义描述结构
的组织形式,不分配内存
结构体类型定义的 作用域
类型名,可以用来定
义 变量
8.2 结构体变量的定义
如果在程序中声明了某个结构体类型后,就可以使它来定
义变量,并且可以对变量进行初始化和使用相应的变量。
通常有三种形式来定义一个结构体类型变量 。
1.先定义结构体类型,再定义结构体变量
?一般形式,
struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
……………,};
struct 结构体名 变量名表列 ;
例 1 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];};
struct student stu1,stu2;
例 2 #define STUDENT struct student
STUDENT
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
STUDENT stu1,stu2;
2.定义结构体类型的同时定义结构体变量
?一般形式,
struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
……………,
}变量名表列 ;
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
3.直接定义结构体变量
?一般形式,
struct
{
类型标识符 成员名;
类型标识符 成员名;
……………,
}变量名表列 ;
例 struct
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
用 无名结构体 直接定义
变量 只能一次
4.说明
?结构体类型与结构体变量概念不同
?类型,不分配内存 ; 变量,分配内存
?类型,不能赋值、存取、运算 ; 变量,可以
?结构体可嵌套
?结构体成员名与程序中变量名可相同,不会混淆
?结构体类型及变量的作用域与生存期
例 struct date
{ int month;
int day;
int year;
};
struct student
{ int num;
char name[20];
struct date birthday;
}stu;
num name birthday month day year
例 struct student
{ int num;
char name[20];
struct date
{ int month;
int day;
int year;
}birthday;
}stu;
num name birthday month day year
结构体变量的初始化
结构体变量的初始化是指在定义结构体类型变量的同
时, 给每个结构成员赋初值 。
结构体变量初始化的一般语法形式为,
结构体类型名 结构体变量名 ={初始数据 };
其中:初始数据的个数、顺序、类型均应与定义结构
体时结构成员的个数、顺序、类型保持一致。
struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
……………,
};
struct 结构体名 结构体变量 ={初始数据 };
例 struct student
{ int num;
char name[20];
char sex;
int age;
char addr[30];
};
struct student stu1={112,“Wang Lin”,?M?,19,“200 Beijing Road”};
一般形式,
2.形式二,
struct 结构体名
{
类型标识符 成员名;
类型标识符 成员名;
……………,
}结构体变量 ={初始数据 };
例 struct student
{ int num;
char name[20];
char sex;
int age;
char addr[30];
}stu1={112,“Wang Lin”,?M?,19,“200 Beijing Road”};
3.形式三,
struct
{
类型标识符 成员名;
类型标识符 成员名;
……………,
}结构体变量 ={初始数据 };
例 struct
{ int num;
char name[20];
char sex;
int age;
char addr[30];
}stu1={112,“Wang Lin”,?M?,19,“200 Beijing Road”};
8.3 结构体变量的引用
1.引用规则
? 结构体变量 不能整体引用,只能引用变量 成员
?可以将一个结 构体变量赋值给另一个结构体变量
?结构体嵌套时 逐级引用
成员 (分量 )运算符
优先级, 1
结合性,从左向右
引用方式,结构体变量名,成员名
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
stu1.num=10;
stu1.score=85.5;
stu1.score+=stu2.score;
stu1.age++;
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
printf(“%d,%s,%c,%d,%f,%s\n”,stu1); (?)
stu1={101,“Wan Lin”,?M?,19,87.5,“DaLian”}; (?)
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
if(stu1==stu2)
…….,( ?)
例 struct student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu1,stu2;
stu2=stu1; ( )
例 struct student
{ int num;
char name[20];
struct date
{ int month;
int day;
int year;
}birthday;
}stu1,stu2;
num name birthday month day year
stu1.birthday.month=12;
【 例 8.1】 对结构体变量的成员进行操作
程序源代码,
#include<iostream.h>
struct date
{
int month;
int day;
int year;
};
struct student
{
int num;
char name[20];
struct date birthday;
char addr[30];
};
void main()
{
student stu1;
stu1.num=1001;
stu1.birthday.month=8;
stu1.birthday.day=20;
stu1.birthday.year=1980;
cout<<stu1.num<<,”;
cout<<stu1.birthday.month
<<" ";
cout<<stu1.birthday.day<<
endl;
}
程序的运行结果为,
1001 8 20 1980
【 例 8.2】 stu1和 stu2是具有相同类型的两个结构体变
量, 将变量 stu1中的成员赋给 stu2中的相应的成员 。
#include<iostream.h>
void main()
{
struct
{ int num;
int age;
}stu1,stu2;
stu1.num=1001;
stu1.age=20;
stu2=stu1;
cout<<stu1.num<<endl;
cout<<stu1.age<<endl;
}
程序的运行结果为,
1001
20
8.4 结构体数组
1.结构体数组的定义
三种形式,
形式一,
struct student
{ int num;
char name[20];
char sex;
int age;
};
struct student stu[2];
num
name
sex
age
num
name
sex
age
stu[0]
stu[1]
25B
形式二,
struct student
{ int num;
char name[20];
char sex;
int age;
}stu[2];
形式三,
struct
{ int num;
char name[20];
char sex;
int age;
}stu[2];
2.结构体数组初始化
分行初始化,
struct student
{ int num;
char name[20];
char sex;
int age;
};
struct student stu[ ]={{100,“Wang Lin”,?M?,20},
{101,“Li Gang”,?M?,19},
{110,“Liu Yan”,?F?,19}};
全部初始化时维数可省
顺序初始化,
struct student
{ int num;
char name[20];
char sex;
int age;
};
struct student stu[ ]={100,“Wang Lin”,?M?,20,
101,“Li Gang”,?M?,19,
110,“Liu Yan”,?F?,19};
例 struct
{ int num;
char name[20];
char sex;
int age;
}stu[ ]={{……},{……},{……}};
例 struct student
{ int num;
char name[20];
char sex;
int age;
}stu[ ]={{……},{……},{……}};
3.结构体数组引用
引用方式,结构体数组名 [下标 ].成员名
struct student
{ int num;
char name[20];
char sex;
int age;
}str[3];
stu[1].age++;
strcpy(stu[0].name,”ZhaoDa”);
结构体数组应用举例
下面通过一个简单的例子来说明结构体数组的定义和使用 。
【 例 8.3】 有一张包含 3名学生的成绩单, 将成绩从大到小排
序输出 。
程序源代码,
#include<iostream.h>
struct student
{
int num;
char name[20];
float score;
}; //定义结构体类型 student
void main()
{
/*定义结构数组 stu,并初始化 */
Student stu[3]={{1001,"Liu Jin",75},{1002,"Li
Lan",82},{1003,"Ma Kai",80}};
student temp;
for (int i=1;i<3;i++)
{
for (int j=0;j<=2-i;j++)
if (stu[j].score<stu[j+1].score)
{ temp=stu[j];
stu[j]=stu[j+1];
stu[j+1]=temp; }
}
}
cout<<"Num"<<" Name"<<" Score"<<endl;
for (int k=0;k<3;k++)
cout<<stu[k].num<<" "<<stu[k].name<<"
"<<stu[k].score<<endl;
}
在此程序中, 排序时交换的数据元素是结构体变量, 当结构
体变量的成员很多时, 这并不是一种好办法 。 为提高效率, 我
们可以使用下一节介绍的结构体指针数组来实现同样的功能 。
程序的运行结果为,
Num Name Score
1002 Li Lan 82
1003 Ma Kai 80
1001 Liu Jin 75
8.5 结构体和指针
1.指向结构体变量的指针
?定义形式,struct 结构体名 *结构体指针名 ;
例 struct student *p;
存放结构体变量在内存
的起始地址
num
name
sex
age
stu
p struct student { int num;
char name[20];
char sex;
int age;
}stu;
struct student *p=&stu;
?使用结构体指针变量引用成员形式
(*结构体指针名 ).成员名 结构体指针名 ->成员名 结构体变量名,成员名
指向运算符
优先级, 1
结合方向:从左向右
2.指向结构体数组的指针
在 C++语言中,把指向结构体数组或数组元素的指针称为
结构体数组指针。
【 例 8.4】 使用结构体数组指针输出数据
例 int n;
int *p=&n;
*p=10; ? n=10
程序源代码,
#include<iostream.h>
struct student
{
int num;
char name[20];
float score;
};
void main()
{student stu[3]={{1001,"Liu
Jin",75},{1002,"Li Lan",82},
{1003,"Ma Kai",80}};
student *s=stu;
//指针 s指向结构体数组的首
地址
cout<<"Num"<<"
Name"<<,Score"<<endl;
for (;s<stu+3;s++)
cout<<s->num<<" " <<s-
>name<<" "<<s-
>score<<endl;
}
注意,
(1) 如果 s的初值为 stu,即指向第一个元素 stu[0]
则 s+1后指向下一个元素 stu[1]的起始地址 。
(2) 程序中已定义了指针 s是指向 struct student 类
型数据的变量, 它只能指向一个 struct student型的
数据, 而不能指向 stu数组元素中的某一个成员 。
程序的运行结果为,
Num Name Score
1001 Liu Jin 75
1002 Li Lan 82
1003 Ma Kai 80
3.用结构体变量和指向结构体的指针作函数参数
在调用函数时,可以把结构体变量的值作为参数
传递给函数。最常用的有三种方法,
用结构体变量的成员作参数。
就是把结构体变量的结构成员作为实参,传递给被调
的函数。其用法和用普通变量作实参是一样的,属
,值传递, 方式。
用结构体变量作实参
就是把结构体变量作为实参,采用, 值传递, 的方式,
结构体变量的全部内容顺序传递给形参,形参也必须
同类型的结构体变量。这种方式有一个缺点,就是在
数调用时,形参也要占用临时的内存单元,结构体成
越多,这种传递方式占用的时间和空间就越大,这将
低程序运行的效率。另外,由于采用, 值传递, 方式,
不能把在被调用的函数中改变了的形参
值带回到主调函数中, 造成使用上的不方
便, 所以经常采用引用作为形参 。
用结构体指针作参数
就是用指向结构体变量或结构体数组的
指针作实参, 将结构体变量或结构体数组
的地址, 传递给指向相同结构体类型的指
针形参 。 函数的返回值可以是整型, 实型
,字符型的值, 也可以是结构体变量或是
结构体指针 。
struct data
{ int a,b,c; };
main()
{ void func(struct data);
struct data arg;
arg.a=27; arg.b=3; arg.c=arg.a+arg.b;
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
printf("Call Func()....\n");
func(arg);
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
}
void func(struct data parm)
{ printf("parm.a=%d parm.b=%d parm.c=%d\n",parm.a,parm.b,parm.c);
printf("Process...\n");
parm.a=18; parm.b=5; parm.c=parm.a*parm.b;
printf("parm.a=%d parm.b=%d parm.c=%d\n",parm.a,parm.b,parm.c);
printf("Return...\n");
}
arg a,27 b,3
c,30
(main)
(func)
parm
a,27
b,3
c,30
copy
arg
a,
b,3
c,30
(func)
parm
a,18
b,5
c,90
a,27
b,3
c,30
例 用结构体变量作函数参数
struct data
{ int a,b,c; };
main()
{ void func(struct data *parm);
struct data arg;
arg.a=27; arg.b=3; arg.c=arg.a+arg.b;
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
printf("Call Func()....\n");
func(&arg);
printf("arg.a=%d arg.b=%d arg.c=%d\n",arg.a,arg.b,arg.c);
}
void func(struct data *parm)
{ printf("parm->a=%d parm->b=%d parm->c=%d\n",parm->a,parm->b,parm->c);
printf("Process...\n");
parm->a=18; parm->b=5; parm->c=parm->a*parm->b;
printf("parm->a=%d parm->b=%d parm->c=%d\n",parm->a,parm->b,parm->c);
printf("Return...\n");
}
arg
a,18
b,5
c,90
(main)
arg
a,27
,3
c,30
ai ) 例 用结构体指针变量作函数参数
arg a,27 b,3
c,30
ai )
(func)
parm ****
arg a,18 b,5
c,9
(main)
(f c)
【 例 8.5】 结构指针做形参
程序源代码,
#include<iostream.h>
struct student
{
int num;
char name[20];
float score;
};
void print(student *ps)
// 形参 ps 被定义为指向
student 类型的指针
{
cout<<ps->num<<" "<<ps-
>name<<" "<<ps-
>score<<endl;
}
void main()
{
student stu[3]={{1001,"Liu
Jin",75},{1002,"LiLan",82},{10
03,"Ma Kai",80}};
for (int i=0;i<3;i++)
{
print(&stu[i]);
//&stu[i]是结构体数组元素
stu[i]的地址
}
程序的运行结果为,
1001 Liu Jin 75
1002 Li Lan 82
1003 Ma Kai 80
8.6 用指针处理链表
§ 8.6.1 链表的概述
§ 链表是一种最常见的数据结构, 分为 动态链表 和 静
态链表 。 程序员经常使用的是动态链表, 它能进行动态
内存分配可以适应数据动态增减的情况, 并且可以方便
地进行数据元素的插入, 删除等操作 。
§ 链表有 单向链表, 双向链表, 循环链表 等形式 。 图
8.2是一个单向链表的结构示意图 。
A
1010
0050
B
1300
C
1456
D
NULL
head 0050 1010 1300 1456
图 8.1 单向链表的情况
每个链表都有一个, 头指针, 变量, 在图中以 head表示, 它
存放一个地址, 指向链表的第一个元素 。 链表中每一个元素称
为, 结点 (node)”,每个结点都应包括两个部分:第一部分是
链表中保存的用户需要用的实际数据, 在图中以 A,B,C,D
表示, 第二部分是一个地址, 指向下一个结点 。 可以看出, 头
指针 head指向第一个结点, 第一个结点又指向第二个结点, 依
次类推, 直到指向最后一个结点, 该结点不再指向其它结点,
它称为, 表尾,, 它的地址部分存放一个, NULL”(表示空地
址 ),链表到此结束 。
链表中各结点在内存中可以不是连续存放的, 要找到某
一结点, 必须先找到上一个结点, 根据它提供的下一个结点
的地址才能找到下一个结点 。 结点中的地址用指针变量来实
现 。 即:一个结点中应包含一个指针变量, 用它存放下一个
结点的地址 。
用结构体变量作链表中的结点是最合适的, 我们用这个
指针类型成员来存放下一个结点的地址, 因而链表结点数据
可以设计成这样一个结构体类型,
struct student
{ int num;
float score;
student *next;
};
一般形式,※
每个结点数据都属于 student 结构类型, 成员 num
和 score用来存放结点中的有用数据, next指针指向
下一个结点, 即 next指针值为下一个结点的地址 。
用这种方法就可以建立链表, 如图 8.3所示 。
1030
75
1010
90
1020
82
NULL
num
score
next
图 8.3 链表数据结构的建立
8.6.2 动态内存分配
动态链表是动态地为每一个结点分配内存空间即在需要的
时候才开辟一个结点的存储单元, 当某个存储单元不再需要
的时候可以释放 。 在 C++中提供了 new和 delete运算符来完
成动态内存的分配和释放
※ new运算符
new运算符用于申请所需的内存单元, 返回指定类型的一个
指针 。 它的语法格式为,
指针 =new 数据类型 ;
其中, 指针应预先声明, 指针指向的数据类型与 new后面的
数据类型相同 。 若申请成功, 则返回分配单元的首地址赋
给指针;否则 ( 如没有足够的内存空间 ) 返回 0( 一个空指
针 ) 。
例如,
int *p;
p=new int (20) ;
系统为指针 p分配了的内存单元
初始值为 20
也可以用 new运算符申请一块保存数组的内存单元, 即创建
一个数组 。
格式为,
指针 = new 数据类型 [常量表达式 ]
其中, 常量表达式给出数组元素的个数, 指针指向分配的
内存首地址, 指针的类型与 new后的数据类型相同 。
例如,
int *p;
p=new int[20];
系统为指针 p分配了整型数组的
内存,数组中有 20个元素
※ delete运算符
当程序中不再使用由运算符 new申请的某个内存单元时, 可
以用 delete运算符释放它 。
它的语法格式为,
delete 指针
delete [常量 ]指针
它的功能是释放由 new申请到的内存单元 。 其
中, 指针是指向需要释放的内存单元的指针名
字, 并且 delete只是删除动态内存单元, 并不
是将指针本身删除 。
例如,
int *p1= new int,
*p2= new int [20];
delete p1;
delete [] p2;
说明,
在程序中每次使用 new运算符, 都相应的使用 delete运算符来
释放申请的内存 。 并且每个 new运算符, 只能调用一次 delete来
释放内存, 否则有可能会导致系统错误 。
运算符 delete必须用于先前使用 new分配的有效指针 。
8.6.3 建立单向动态链表
下面通过一个例子来说明如何建立一个单向动态链表
【 例 8.6】 写一个函数, 建立一个有 5名学生数据 (包含学
号和成绩 )的单向动态链表 。
先分析如何设计实现此要求的算法 。
假设输入的学号为 0时, 表示建立链表的过程结束, 该结点不
应链接到链表中 。 根据题目要求, 链表中结点数据应采用以下
结构体类型来描述,
struct student
{ long num;
float score;
struct student *next;
};
同时定义三个指针变量 head,p1,p2,它们都是指向
struct student 结构体类型的指针变量 。 结构指针变量 head
的初值为 NULL(即等于 0),此时是空链表 (head不指向任何
结点, 链表中无结点 ),当链表建成后, 应使 head指向第一
个结点 。
※ 首先利用 new运算符, 在内存中开辟一个存储空间, 用
来存放新结点 。 使 p1,p2都指向该存储空间, 然后从键盘
上输入一个学生的数据进行判断, 如果输入的 p1->num不
等于 0,而且是第一个结点数据 ( n= 1), 则把 p1的值赋给
head(head=p1),这样, 结构指针 head就指向了链表中的第
一个结点, 见图 8.4。
p1
1010
90
head
p2
(n=1)
图 8.4 建立表头结点
※ 然后再开辟一个新的存储空间, 用来存放另一个结点, 并
使 p1指向新开辟的存储空间, 然后输入该结点的数据 。 见图
8.5( a) 0。 如果输入的 p1-> num不等于 0,而且不是第一
个结点 ( n≠1) 时, 则应将新建结点与前一个结点连接在一起
,也就是说, 执行 p2-> next=p1,使第一个结点的 next 成员
指向第二个结点, 见图 8.5( b) 。 接着使 p2= p1,也就是使
p2指向刚才建立的结点, 见图 8.5( c) 。
※ 重复步骤 ( 2), 依次建立若干个新结点 。 每次都让 p1指
向新建立的结点, p2指向链表中最后一个结点, 然后用, p2
-> next=p1”,把 p1所指的结点连接到 p2所指结点的后面 。
※ 当输入某个结点数据后, 如果 p1-> num等于 0,则不再
执行上述循环, 此新结点不应该被连接到链表中, 用语句
,p2-> next=NULL”,将 NULL值赋给前一个结点的 next 成
员, 如图 8.6所示 。
head
p2
1010
90
p1
1030
75
(a)
head
p2
1010
90
p1
1030
75 (n=2)
(b)
head 1010
90
p1
1030
75
p2
(n=2)
(c)
图 8.5 建立第二个结点
head 1010
90
P1
1030
75
1020
82
NULL
0
0
P2
图 8.6 链表中最后一个结点的指针域为空
至此,建立链表的过程结束。
建立链表的函数如下,
#include<iostream.h>
struct student
{
long num;
float score;
struct student *next;
};
int n=0; //n为结点个数, 初值为 0
struct student *creat()
{
struct student *head,*p1,*p2;
head=NULL;
//在没有创建任何结点时, 表头
指向空
p1=new(student);
//创建一个新结点
p2=p1;
cin>>p1->num>>
p1->score;
//输入第一个结点数据
while (p1->num!=0)
{
n++;
p2->next=p1;
//原表尾结点所指向的下一
个结点应为新建结点
p2=p1;
//新建结点成为新的表尾结

p1=new(student);
cin>>p1->num>>
p1->score;
}
delete(p1);
//对于 num=0的结点, 不应被
连接到链表中, 应删除
p2->next=NULL;
//输入结束, 表尾结点所指
向的下一个结点应为空
return(head);
}
程序说明,
定义了一个 student结构体类型, 链表中每个结
点数据都属于该类型, 因而 next也应是一个
struct student类型的结构体指针, 它存放下一个
结点的地址 。
定义了一个函数 creat(),函数返回一个指向链
表头结点的指针值 。


8.6.4 输出链表
输出链表就是将链表中各结点的数据依次输出 。 首先要知道链表第一个结点的地址, 也就是要知道头结点 head的
值然后设一个指针变量 p,让 p先指向第一个结点, 输出 p所
指结点的数据, 再使 p后移一个结点, 再输出其数据, 直到
链表的尾结点为止 。

【 例 8.7】 编写一个输出链表数据的函数 。
void print(struct student *head)
{ struct student *p;
p=head;
if (p==NULL) return;
do
{
cout<<p->num<<" "<<p->score<<endl;
p=p->next; //使 p指向下一个结点
}while (p!=NULL);
}
head的值由实参传过来, 也就是将已有的链表的头指针
传给被调用的函数, 在 print函数中从 head所指的第一个
结点开始顺序输出每个结点 。
8.6.5 对链表的删除操作
本小节所讲的对链表的删除操作是把某个结点 从链表中
摘除, 并不是真正从内存中将这个结点删除掉, 使它脱
离原来的链表, 解除原来的链接关系 。
算法分析,
以指定的学号为删除标志 。 从指针变量 p指向的第
一个结点开始, 检查该结点中的 num是否为要删除的
学号, 如果是则将其删除;如果不是, 则将 p移到下
一个结点, 再继续判断, 直到删除或到表尾为止
执行过程,
设两个指针 p1和 p2,先使 p1指向第一个结点如果 p1所
指的结点不是要删除的结点, 就将 p2指向 p1所指的结点 (
p2=p1), 然后将 p1指向下一个结点 ( p1=p1->next) 。 再
继续判断 p1所指的结点是不是要删除的结点, 如此重复,
直到找到要删除的结点并将其删除或是检查完全部链表为
止 。 要删除的结点分两种情况,
(1) 要删除的是第一个结点 ( 即 p1==head), 则执行
head=p1->next。 这时, head指向了原来的第二个结点 。
此时, 第一个结点虽然还存在, 但它已与链表脱离, 因为
链表中没有一个结点或头指针指向它, 也就不能访问它了
,即已被删除 。
(2) 要删除的不是第一个结点, 则应执行 p2->next=p1-
>next,即 p2->next指向了 p1->next所指向的结点, p1所指
向的结点就被删除而不再是链表的成员了 。
删除结点的函数如下,
struct student *delete(struct student *head,long num)
{
struct student *p1,*p2;
if (head==NULL)
{cout<<"list null"<<endl;return head;}
p1=head;
while (num!=p1->num && p1->next!=NULL)
//p1指向的不是所要找的结点, 并且后面还有结点
{
p2=p1;
if (num==p1->num) //找到了
{ if (p1==head)
head=p1->next;
//若 p1指向的是头结点, 则 head指向第二个结点
else p2->next=p1->next;
//否则将下一个结点地址赋给前一结点地址
cout<<"delete:"<<num;
n=n-1; //链表结点个数减 1
}
else
cout<<num<<"not been found!"<<endl;
return(head); }
函数的返回值是链表的头指针 。 函数的参数是 head和要删除
的学号 num,head的值可能在函数执行过程中被改变 ( 当删
除第一个结点时 ) 。
8.6.6 对链表的插入操作
对链表的插入是指将一个结点插入到一个已有的链表中
【 例 8.9】 编写一个插入结点到链表中的函数 。
为简单起见,假设有一个学生链表,各结点已按其成员学
号( num)的值由小到大顺序排列,现在要插入一个学生
的结点,要求按学号的顺序插入。
过程分析,
先将要插入的结点学号与第一个结点的学号相比, 若小
则插入到第一个结点前面, 否则与第二个结点相比, 如此
重复, 直到找到一个比它大的学号插入到它的前面或链表
结束插入到链表的尾部 。
过程实现,
先用指针变量 p0指向待插入的结点, p1指向第一个结点
。 将 p0->num与 p1->num相比, 如果 p0->num> p1->num
,就让 p2指向 p1所指的结点, 然后将 p1后移 。 再将 p0-
>num与 p1->num相比, 若 p0->num仍然大, 则 p1继续后
移, 直到 p0->num<= p1->num为止 。 这时将 p0所指的
结点插入到 p1所指结点的前面 。 如果 p0->num比所有结
点的 num都大, 则应将 p0所指的结点插入到链表的尾部

如果插入的位置在链表的中间, 则将 p0的值赋给 p2-
>next,使 p2->next指向待插入的结点, 然后将 p1的值赋
给 p0->next,使 p0->next指向 p1所指的变量, 见图 。
如果插入位置在第一个结点前面, 则将 p0赋给 head,将
p1赋给 p0->next。 如果要插到表尾之后, 应将 p0赋给 p1-
>next,NULL赋给 p0->next。
插入函数如下,
struct student *insert(struct student *head,struct student
*stud)
{ struct student *p0,*p1,*p2;
p1=head; //p1指向第一个结点
p0=stud; //p0指向待插入的结点
if (head==NULL) //原来的链表是空表
{head=p0;p0->next=NULL;}
//使 p0指向的结点作为头结点
else
while ((p0->num>p1->num) && (p1->next!=NULL)
{ p2=p1;
//p2指向 p1所指的结点
p1=p1->next;
//p1后移一个结点
}
if (p0->num<=p1->num)
{ if(head==p1)
head=p0;
//插入到第一个结点前面
else
p2->next=p0;
//插入到 p2指向的结点后面
p0->next=p1;
} else
{
p1->next=p0;
p0->next=NULL;
//插入到最后的结点后面
}
n=n+1;
//链表结点个数加 1
return (head);
}
8.7 共用体
?构造数据类型,也叫联合体
?用途:使几个不同类型的变量共占一段内存 (相互覆盖 )
1.共用体类型定义
定义形式,
union 共用体名
{
类型标识符 成员名;
类型标识符 成员名;
……………,
}; 例 union data
{ int i;
char ch;
float f;
}; f
ch
i 类型定义 不分配内存
union是关键字
形式一,
union data
{ int i;
char ch;
float f;
}a,b;
形式二,
union data
{ int i;
char ch;
float f;
};
union data a,b,c,*p,d[3];
形式三,
union
{ int i;
char ch;
float f;
}a,b,c;
1.共用体变量的定义
f
ch
i
f
ch
i
a b
共用体 变量定义 分配内存,
长度 =最长成员 所占字节数
共用体 变量任何时刻
只有 一个成员 存在
2.共用体变量引用
引用方式,
共用体指针名 ->成员名 共用体变量名,成员名 (*共用体指针名 ).成员名
union data
{ int i;
char ch;
float f;
};
union data a,b,c,*p,d[3];
a.i a.ch a.f
p->i p->ch p->f
(*p).i (*p).ch (*p).f
d[0].i d[0].ch d[0].f
例 a.i=1;
a.ch=?a?;
a.f=1.5;
printf(“%d”,a.i); (?编译通过,运行结果不对 )
?引用规则
?不能引用共用体变量,只能 引用其成员
?共用体变量中起作用的成员是 最后一次存放的成员
例 union
{ int i;
char ch;
float f;
}a;
a=1; (?)
?不能 在定义共用体变量时 初始化
例 union
{ int i;
char ch;
float f;
}a={1,?a?,1.5}; (?)
?可以用一个共用体变量为另一个变量赋值
例 float x;
union
{ int i; char ch; float f;
}a,b;
a.i=1; a.ch=?a?; a.f=1.5;
b=a; (?)
x=a.f; (?)
例 将一个整数按字节输出
01100001 01000001
低字节 高字节
01000001
01100001
ch[0]
ch[1]
运行结果,
i=60501
ch0=101,ch1=141
ch0=A,ch1=a
main()
{ union int_char
{ int i;
char ch[2];
}x;
x.i=24897;
printf("i=%o\n",x.i);
printf("ch0=%o,ch1=%o\n
ch0=%c,ch1=%c\n",
x.ch[0],x.ch[1],x.ch[0],x.ch[1]);
}
3.结构体与共用体
区别, 存储方式不同
struct node
{ char ch[2];
int k;
}a;
union node
{ char ch[2];
int k;
}b;
a ch k
b ch k
变量的各成员同时存在
任一时刻只有一个成员存在
?联系, 两者可相互嵌套
例 结构体中嵌套共用体
name num sex job class position
Li
Wang
1011
2086
F
M
S
T
501
prof
循环 n次
读入姓名、号码、性别、职务
job==?s? 真
真 假

读入 class 读入
position
输出
“输入错”
循环 n次
job==?s? 真 假
输出,姓名,号码,
性别,职业,班级
输出,姓名,号码,
性别,职业,职务
job==?t?
struct
{ int num;
char name[10];
char sex;
char job;
union
{ int class;
char position[10];
}category;
}person[2];
例共用体中嵌套结构体,机器字数据与字节数据的处理
00010010 00110100
低字节 高字节
00110100
00010010
low
high
0x1234
00010010 11111111
低字节 高字节
11111111
00010010
low
high
0x12ff
struct w_tag
{ char low;
char high;
};
union u_tag
{ struct w_tag byte_acc;
int word_acc;
}u_acc;
word_acc byte_acc.low byte_acc.high
u_acc
8.8 枚举类型
8.8.1 枚举类型及枚举变量的定义
?枚举是指将变量的取值 一一列举出来,量的取值
只限于列举出来的值的范围内。
枚举类型定义的一般形式为,
enum 枚举类型名
{ 枚举元素 1,
枚举元素 2,……,
枚举元素 n
} ;
enum是定义枚举
类型的关键字
enum
weekday{sun,mon,tue,w
ed,thu,fri,sat}
例,
说明,
(1) 在 C++语言中, 对枚举元素按常量处理, 故称枚举常量
它们不是变量, 除在定义时对其赋值外, 在程序中不能对它
们赋值 。
例如,
sun=0;mon=1;
是错误的 。
(2) 枚举元素作为常量, 它们是有值的 。 在定义时枚举元素若
无赋值, 编译系统自动按定义时的顺序使它们的值分别为 0,
1,2,…… ;各枚举元素若有赋值, 则以赋值为准;定义中
若对某个枚举元素赋值, 其后省略赋值者, 以此赋值为基础
顺序加 1。
例如,
enum weekday{sun,mon,tue,wed=5,thu,fri,sat};
// sun=0,mon=1,tue=2,wed=5,thu=6,fri=7,sat=8
(3) 在定义中各枚举元素不能重名, 程序中其它标识符也不
能与枚举元素重名 。
与结构体类型变量, 共用体类型变量的定义形式相似, 枚
举类型的变量定义方式也有三种形式,
形式一,
enum weekday{sun,mon,tue,wed=5,thu,fri,sat};
weekday workday,week_end;
形式二,enum
weekday{sun,mon,tue,wed=5,thu,fri,sat}workday,week_end
形式三:
enum{sun,mon,tue,wed=5,thu,fri,sat}workday,week_end
8.8.2 枚举元素的引用
?枚举元素作为整型常量, 可以直接赋给枚举变量或直接引用 。
Enum
weekday{sun,mon,tue,wed,thu,fri,sat}wd;
wd=mon;
cout<<wed<<endl; 枚 举 元 素
赋 值 给 枚
举变量
直接引用枚举元素
?一个整数不能直接赋值给一个枚举变量 。 因为它们
属于不同的类型, 应进行强制类型转换后才能赋值 。
wd=weekday(5);
//强制类型转换 ( √)
这相当于将顺序号为 5的枚举元素 (fri)赋给 workday。
?同类型的枚举变量, 枚举元素之间可以进行
算术运算, 关系运算 。
?枚举元素和枚举变量可以作为函数的参数,
函数的返回值也可以是枚举类型 。
【 例 8.11】 输入两个整数,依次求出它们的和、差、积并
输出。要求用枚举类型数据来处理和、差、积的判断
#include <iostream.h>
void main()
{ enum en{plus,minus,times}op1;
int x,y;
cin>>x>>y;
op1=plus;
while (op1<=times)
{
switch (op1)
{
case plus,cout<<x<<" "<<y<<"
"<<x+y<<endl;break;
case minus:cout<<x<<" "<<y<<" "<<
x-y<<endl;break;
case times:cout<<x<<" "<<y<<
" "<<x*y<<endl;break;
}
int i=(int)op1;
//强制类型转换成 int型, 因为枚举变量不能自增
op1=en(++i);
}
}
11.10 用 typedef定义类型
1,功能,用自定义名字为 已有 数据类型命名
2,类型定义 简单形式,typedef type name;
例 typedef int INTEGER;
类型定义语句
关键字 已有数据类型 名
用户定义的类
型名
例 typedef float REAL;
类型定义后,与已有类型一样使用
例 INTEGER a,b,c;
REAL f1,f2;
int a,b,c;
float f1,f2;
说明,
1.typedef 没有创造 新数据类型
2.typedef 是定义类型,不能定义变量
3.typedef 与 define 不同
define typedef
预编译时处理 编译时处理
简单字符置换 为已有类型命名
3.typedef定义类型步骤
?按定义变量方法先写出定义体 如 int i;
?将变量名换成新类型名 如 int INTEGER;
?最前面加 typedef 如 typedef int INTEGER;
?用新类型名定义变量 如 INTEGER i,j;
例 定义数组类型
? int a[100];
? int ARRAY[100];
? typedef int ARRAY[100];
? ARRAY a,b,c;
? int a[100],b[100],c[100];
4.类型定义可嵌套
例 定义指针类型
? char *str;
? char *STRING;
? typedef char *STRING;
?STRING p,s[10];
? char *p;
char *s[10];
例 定义结构体类型
? struct date
{ int month;
int day;
int year;
}d;
例 定义函数指针类型
? int (*p)();
? int (*POWER)();
? typedef int (*POWER)();
?POWER p1,p2;
? int (*p1)(),(*p2)();
例 定义结构体类型
? DATE birthday,*p;
? struct date
{ int month;
int day;
int year;
}birthday,*p;
例 定义结构体类型
?typedef struct date
{ int month;
int day;
int year;
}DATE;
例 定义结构体类型
? struct date
{ int month;
int day;
int year;
}DATE;
例 typedef struct club
{ char name[20];
int size;
int year;
}GROUP;
typedef GROUP *PG;
PG pclub; ? GROUP *pclub; ? struct club *pclub;
GROUP为结构体类型
PG为指向 GROUP的指针类型