1
第 9章 结构体与共用体北京科技大学 计算机系
C 语言程序设计
2009-8-17
2第 9章 结构体与共用体
9.1 结构体
9.2 共用体
9.3 枚举类型与类型命名
2009-8-17
3
一个学生的信息有 学号,姓名,性别,年龄,住址,成绩 等 。
一本图书的信息有 分类编号,书名,作者,出版社,出版日期,价格,库存量 等 。
如何描述这些类型不同的相关数据?
9.1 结构体
9.1.1 结构体与结构体类型的定义信息管理结构体 ——一种构造类型数据结构体 由若干不同类型的数据项组成,
构成结构体的各个数据项称为 结构体成员 。
2009-8-17
4
struct 结构体名
{ 数据类型 1 成员名 1;
数据类型 2 成员名 2;
……
数据类型 n 成员名 n;
};
9.1 结构体
9.1.1 结构体与结构体类型的定义结构体类型定义的一般形式:
struct为关键字;
结构体名 是用户定义的 类型标识 。
{ }中是组成该结构体的 成员 。 成员的 数据类型 可以是 C语言所允许的任何数据类型 。
2009-8-17
5
例如图书类型的定义:
struct bookcard
{ char num[10]; /*图书 分类编号是字符数组类型 */
char name[30]; /*书名是字符数组类型 */
char author[30]; /*作者是字符数组类型 */
char publisher[60]; /*出版社是字符数组类型 */
float price; /*价格是单精度实型 */
int n; /*库存量是整型 */
};
例如学生类型的定义:
struct student
{ char num[8]; /* 学号是 字符数组 类型 */
char na e[30]; /* 姓名是 字符数组 类型 */
sex; /* 性别是 字符型 */
int age; /* 年龄是 整型 */
char addr[60]; /* 住址是 字符数组 类型 */
int score[6]; /* 成绩是 整型数组 类型 */
};
9.1 结构体
9.1.1 结构体与结构体类型的定义
2009-8-17
69.1 结构体
9.1.2 结构体变量的定义与初始化
1,结构体类型变量的定义
利用已 定义的结构体类型名定义变量
struct 结构体名 变量名表;
例如:
struct bookcard book1[100];
struct student s[30],t1,t2;
按照结构体类型的组成,系统为定义的结构体变量分配内存单元 。 结构体变量的各个成员在内存中占用连续存储区域,结构体变量 所占内存 大小 为结构体中 每个成员 所占用内存的 长度之和 。
struct student
num 8个字节
name 30个字节
sex 1个字节
age 2个字节
addr 60个字节
score 12个字节
2009-8-17
79.1 结构体结构体类型与变量的说明
类型 与 变量 是不同的概念。
应先定义一个 结构体类型,而后再定义 结构体变量 。
系统对 类型 不分配空间,仅对 变量 分配空间。
只能对 变量 赋值、存取或运算,而不能对一个 类型赋值、存取或运算。
成员 也可以是 结构变量 。
对结构中的 成员,可以单独使用,它的作用与地位相当于 普通变量 。
成员名 可与程序中的变量名相同时,也可与 不同结构体类型的成员名相同,二者代表不同的对象。
struct date
{ int year,month,day;};
struct student
{ char num[8];
char name[30];
char sex;
struct date birthday; /* 成员为结构体类型 */
char addr[60];
int score[6];
};
num 8个字节
name 30个字节
sex 1个字节
birthday
year 2个字节
month 2个字节
day 2个字节
addr 60个字节
score 12个字节
2009-8-17
89.1 结构体
9.1.2 结构体变量的定义与初始化
1,结构体类型变量的定义
在定义结构体类型的同时定义变量例如:
struct student
{ char num[8],name[20],sex;
int age;
float score;
}st[30];
struct 结构体名
{ 成员定义表;
}变量名表;
2009-8-17
99.1 结构体
9.1.2 结构体变量的定义与初始化
1,结构体类型变量的定义
直接定义结构体类型变量例如:
struct
{ char num[8],name[20],sex;
int age;
float score;
}st[30],a,b,c;
struct
{ 成员定义表;
}变量名表;
2009-8-17
109.1 结构体
9.1.2 结构体变量的定义与初始化
2,结构体变量的初始化
【 例 9.4】 结构体变量的初始化 。
struct date
{ int year,month,day;};
struct student
{ char num[8],name[20],sex;
struct date birthday;
float score;
}a={"9606011","Li ming",'M',{1977,12,9},83},
b={"9608025","Zhang liming",'F',{1978,5,10},87},c;
如果初值个数少于结构体成员个数,
则将无初值对应的成员赋以 0值。
如果初值个数多于结构体成员个数,
则编译出错。
2009-8-17
119.1 结构体
9.1.2 结构体变量的定义与初始化
2,结构体变量的初始化
【 例 9.5】 结构体数组的初始化 。
struct s
{ char num[8],name[20],sex;
float score;
}stu[3]={{"9606011","Li ming",'M',87.5},
{"9606012","Zhang jiangguo",'M',79},
{"9606013","Wang ping",'F',90}};
元素的个数可以省略,根据赋初值时结构体常量的个数确定数组元素的个数
2009-8-17
129.1 结构体
9.1.2 结构体变量的定义与初始化
3,结构体变量的运算
用 sizeof运算符计算结构体变量所占内存空间
struct date
{ int year,month,day;};
struct student
{ char num[8],name[20],sex;
struct date birthday;
float score;
}a;
sizeof(a) 的结果为 8+20+1+6+4=39
sizeof(struct student) 的结果为 39
2009-8-17
139.1 结构体
9.1.2 结构体变量的定义与初始化
3,结构体变量的运算
同类型结构体变量之间的 赋值运算结构体变量之间进行赋值时,系统将按成员一一对应赋值。
struct date
{ int year,month,day;};
struct student
{ char num[8],name[20],sex;
struct date birthday;
float score;
}a={"9606011","Li ming",'M',{1977,12,9},83},b,c;
c = a;
2009-8-17
149.1 结构体
9.1.2 结构体变量的定义与初始化
3,结构体变量的运算
对结构体变量进行 取址运算
struct date
{ int year,month,day;};
struct student
{ char num[8],name[20],sex;
struct date birthday;
float score;
}a;
对结构体变量 a进行 &a 运算,可以得到 a的首地址,它是结构体类型指针 。
2009-8-17
159.1 结构体
9.1.2 结构体变量的定义与初始化
4,结构体变量成员的引用结构体变量成员引用的一般形式:
结构体变量名,成员名结构体变量 a的各成员可分别表示为 a.num、
a.name,a.sex,a.birthday,a.score
struct date
{ int year,month,day;};
struct student
{ char num[8],name[20],sex;
struct date birthday;
float score;
}a;
“.”是分量运算符,运算级别最高。
a.birthday.year
a.birthday.month
a.birthday.day
结构体变量的各个成员可进行何种运算,由该成员的数据类型决定
2009-8-17
169.1 结构体
9.1.2 结构体变量的定义与初始化
【 例 9.6】 编写一个统计选票的程序 。
struct candidate
{ char name[20]; /* name为候选人姓名 */
int count; /* count为候选人得票数 */
} list[ ]={{"invalid",0},{"Zhao",0},{"Qian",0},
{"Sun",0},{"Li",0},{"Zhou",0}};
2009-8-17
179.1 结构体
9.1.2 结构体变量的定义与初始化
main( )
{ int i,n;
printf("Enter vote\n");
scanf("%d",&n); /* 输入所投候选人编号,编号从 1开始 */
while (n!=-1) /* 当输入编号为 -1时,表示投票结束 */
{ if (n>=1 && n<=5)
list[n].count++; /* 有效票,则相应候选人计票成员加 1*/
else
{ printf("invalid\n");
list[0].count++; } /* 无效票,list[0]的计票成员加 1 */
scanf("%d",&n); /* 输入所投候选人编号 */
}
2009-8-17
189.1 结构体
9.1.2 结构体变量的定义与初始化
for (i=1; i<=5; i++)
printf("%s:%d\n",list[i].name,list[i].count);
printf("%s:%d\n",list[0].name,list[0].count);
}
2009-8-17
199.1 结构体
1,结构体指针变量的定义结构体指针变量定义的一般形式:
struct 结构体名 *指针变量名 ;
struct date
{ int year,month,day;}*q;
例如:
struct student *p; p是指向 struct student结构体变量的指针变量
9.1.3 结构体的指针
2009-8-17
209.1 结构体
9.1.3 结构体的指针
2,结构体成员的三种引用形式
用结构体变量名的引用形式:
d.year d.month d.day
struct date
{ int year,month,day;}d,*p=&d;
用结构体指针变量的引用形式,
(*p).year (*p).month (*p).day
p->year p->month p->day
,->” 是指向结构体成员运算符,优先级为一级
p=&d.year×
2009-8-17
219.1 结构体
9.1.3 结构体的指针
【 例 9_a】 输入今天的日期,然后输出该日期 。
main( )
{ struct date /* 在函数中定义结构体类型 */
{ int year,month,day;
}today,*p=&today; /* 定义结构体变量及其指针 */
printf ("Enter today date(YYYY/MM/DD):");
scanf("%d/%d/%d",&today.year,&today.month,
&today.day);
printf("Today:%d/%d/%d\n",p->year,p->month,
p->day);
}
2009-8-17
229.1 结构体
9.1.3 结构体的指针
3,指向结构体数组的指针
【 例 9.7】 利用结构体指针输出一组化学元素名称及其原子量 。
struct list
{ int i;
char name[4];
float w;
}tab[4]={{1,"H",1.008},{2,"He",4.0026},
{3,"Li",6.941},{4,"Be",9.01218}};
tab数组
1
H tab[0]
1.008
2
He tab[1]
4.0026
3
Li tab[2]
6.941
4
Be tab[3]
9.01218
2009-8-17
239.1 结构体
9.1.3 结构体的指针
3,指向结构体数组的指针
main( )
{ struct list *p;
printf("No\tName\tAtomic Weight\n");
for (p=tab; p<tab+4; p++)
printf("%d\t %s\t %f\n",
p->i,p->name,p->w);
}
tab数组
1
H tab[0]
1.008
2
He tab[1]
4.0026
3
Li tab[2]
6.941
4
Be tab[3]
9.01218
p
p
p
p
p
No Name Atomic Weight
1 H 1.008
2 He 4.0026
3 Li 6.941
4 Be 9.01218
2009-8-17
249.1 结构体
9.1.3 结构体的指针
【 例 9.8】 分析自增自减运算对程序结果的影响 。
struct code
{ int i;
char c;
}a[ ]={{100,'A'},{200,'B'},
{300,'C'},{400,'D'}};
a数组
100 a[0]
'A'
200 a[1]
'B'
300 a[2]
'C'
400 a[3]
'D'
2009-8-17
259.1 结构体
9.1.3 结构体的指针
main( )
{ struct code *p=a;
printf("%d\t",++p->i);
printf("%c\t",(++p)->c);
printf("%d\t",(p++)->i);
printf("%c\t",++p->c);
printf("%d\t",p->i++);
printf("%d\n",p->i);
}
a数组
100 a[0]
'A'
200 a[1]
'B'
300 a[2]
'C'
400 a[3]
'D'
p 101
D'
301
101 B 200 D 300 301
2009-8-17
269.1 结构体
9.1.4 函数间结构体数据的传递
方法一:在函数之间直接传递结构体数据 。
函数的 形参 定义为 结构体变量 。 函数调用时,
可 将主调函数的 结构体类型 实参 传递给被调函数的 形参 。
如果 将函数定义为 结构体类型函数,可 利用
return语句将一个结构体数据结果返回到主调函数中 。
2009-8-17
279.1 结构体
9.1.4 函数间结构体数据的传递
方法二:在函数之间传递结构体指针 。
形参 定义为 指向结构体类型的 指针 变量,可 将主调函数的 结构体指针 传递给被调函数的 形参 变量,通过指针形参的指向域的扩展,操作 主调函数中 结构体变量及其成员 。
如果 将函数定义为 结构体指针型函数,可 利用
return语句将被调函数中结构体变量的 指针 返回给主调函数的 结构体指针变量 。
方法三:利用 全局结构体变量 传递结构体数据 。
2009-8-17
289.1 结构体
9.1.4 函数间结构体数据的传递
【 例 9.9】 编制一个复数乘法函数,采用值传递的方法传送数据 。
struct complex /* 定义存放复数的结构体类型 */
{ float re; /* re成员用于存放复数的实部 */
float im; /* im成员用于存放复数的虚部 */
};
2009-8-17
299.1 结构体
9.1.4 函数间结构体数据的传递
struct complex multiplier(struct complex cx,
struct complex cy)
{ struct complex cz;
cz.re=cx.re*cy.re-cx.im*cy.im;
cz.im=cx.re*cy.im+cx.im*cy.re;
return(cz);
}
形参是结构体变量。调用此函数时,
系统将分别为形参 cx和 cy各分配一个
sizeof(struct complex) 大小的内存空间,
每个成员都要一一传递。
2009-8-17
309.1 结构体
9.1.4 函数间结构体数据的传递
main( )
{ struct complex x,y,z;
x.re=3.2;
x.im=1.5;
y.re=2.7;
y.im=4.6;
z=multiplier(x,y);
printf("%f+%fi\n",z.re,z.im); /*以复数形式输出 */
}
2009-8-17
319.1 结构体
9.1.4 函数间结构体数据的传递
【 例 9.10】 编制一个复数乘法函数,采用传递指针的方法达到传送数据的目的 。
struct complex
{ float re,im;};
void multiplier(struct complex *px,
struct complex *py,struct complex *pz)
{ pz->re=px->re*py->re-px->im*py->im;
pz->im=px->re*py->im+px->im*py->re;
}
形参定义为指针型参数。函数调用时,实参传递的是结构体指针(地址),因此形参 px,py可读取主调函数中变量的内容,乘积结果也可通过形参 pz指针存到主调函数中的目标变量。
这样实参与形参之间的数据传递由多值(每个成员的值)
变成了单值(结构体变量的首地址)。
2009-8-17
329.1 结构体
9.1.4 函数间结构体数据的传递
main( )
{ struct complex x,y,z;
x.re=3.2;
x.im=1.5;
y.re=2.7;
y.im=4.6;
multiplier(&x,&y,&z);
printf("(%f+%fi)*(%f+%fi)=%f+%fi\n",x.re,x.im,
y.re,y.im,z.re,z.im);
}
2009-8-17
33
链表是一种动态数据结构,可根据需要动态地分配存储单元。在数组中,插入或删除一个元素都比较繁琐,而用链表则相对容易。但是数组元素的引用比较简单,对于链表中结点数据的存取操作则相对复杂。
9.1 结构体
9.1.5 结构体的应用 —— 链表
① 链表中每个元素称为一个结点 。
② 构成链表的结点必须是结构体类型数据 。
1,链表的基本结构
head 1000 1032 3284 1296 1382 2008
图 9.2 动态单向链表示意图
C
3284
H
1296
A
1382
I
2008
N
NULL
1000 1032
③ 相邻结点的地址不一定是连续的,依靠指针将它们连接起来 。
struct node
{char c;
struct node *next; };
2009-8-17
34
C语言提供了相关的存储管理库函数。这里仅介绍其中三个,它们的原型说明在
,stdlib.h”头文件和,alloc.h”头文件中,使用这三个函数时,应选择其中一个头文件包含到源程序中。
⑴ 动态分配存储区函数 malloc( )
函数原型,void *malloc(unsigned size);
调用格式,malloc(size)
功能:在内存分配一个 size字节的存储区。调用结果为新分配的存储区的首地址,是一个 void
类型指针。若分配 失败,则返回 NULL( 即 0) 。
9.1 结构体
9.1.5 结构体的应用 —— 链表
2,动态分配和释放存储单元在 ANSI C标准中,关键字 void有两种用法 。
第一种用法,可将无返回值的函数定义为 void类型第二种用法,用 void * 定义指针,这是一个指向非具体数据类型的指针,称为无类型指针 。
2009-8-17
35
【 例 9.11】 调用 malloc函数分配所需存储单元 。
#include <stdlib.h>
main( )
{ struct st
{ int n;
struct st *next;
}*p;
p=(struct st *)malloc(sizeof(struct st));
p->n=5; p->next=NULL;
printf("p->n=%d\tp->next=%x\n",p->n,p->next);
}
9.1 结构体
9.1.5 结构体的应用 —— 链表
2,动态分配和释放存储单元将函数返回值转换成结构体指针
2009-8-17
36
⑵ 动态分配存储区函数 calloc( )
函数原型:
void *calloc(unsigned int n,unsigned int size);
调用格式,calloc(n,size)
功能:在内存分配一个 n倍 size字节的存储区。
调用结果为新分配的存储区的首地址,是一个 void
类型指针。若分配 失败,则返回 NULL( 即 0) 。
9.1 结构体
9.1.5 结构体的应用 —— 链表
2,动态分配和释放存储单元
2009-8-17
37
【 例 9.12】 调用 calloc函数分配所需存储单元 。
#include <stdlib.h>
main( )
{ int i,*ip;
ip=(int *)calloc(10,2);
for (i=0; i<10; i++)
scanf("%d",ip+i);
for (i=0; i<10; i++)
printf("%d ",*(ip+i));
printf("\n");
}
9.1 结构体
9.1.5 结构体的应用 —— 链表
2,动态分配和释放存储单元动态分配了 10个存放整型数据的存储单元
2009-8-17
38
⑶ 释放动态分配存储区函数 free( )
函数原型,void free(void *p);
9.1 结构体
9.1.5 结构体的应用 —— 链表
2,动态分配和释放存储单元 此函数无 返回值实参必须是一个指向动态分配存储区的指针,它可以是任何类型的指针变量。
调用格式,free(p)
功能:释放 p所指向的动态分配的存储区 。
2009-8-17
39
q
建立链表就是根据需要一个一个地开辟新结点,在结点中存放数据并建立结点之间的链接关系。
【例 9.13】建立一个学生电话簿的单向链表函数。
9.1 结构体
9.1.5 结构体的应用 —— 链表
3,建立单向链表 头指针 h设为 NULL
读入一个学生姓名当姓名长度不为 0
开辟新结点 p=NEW
strcpy(p->name,name)
gets(p->tel)
p->next=NULL
h==NULLT F
h指向第一个 连接新结点结点 h=p q->next=p
q指向新的尾结点 q=p
读入一个学生姓名图 9.3 建立单向链表
NULL
h
p
Chang
62783410
NULL
Wang
63212986
NULL
p
q
2009-8-17
40
strcpy(p->name,name); /* 为新结点中的成员赋值 */
printf("tel,");
gets(p->tel);
p->next=NULL;
if (h==NULL) /* h为空,表示新结点为第一个结点 */
h=p; /* 头指针指向第一个结点 */
else /* h不为空 */
q->next=p; /* 新结点与尾结点相连接 */
q=p; /* 使 q指向新的尾结点 */
printf("name,");
gets(name);
}
return h;
}
struct node *creat ( )
{ stat c struct node *h;
struct node *p,*q;
char ame[20];
h=NULL;
printf("name,");
gets(name);
while (strlen(name)!=0) /* 当输入的姓名不是空串循环 */
{ p NEW; /* 开辟新结点 */
if (p==NULL) /* p为 NULL,新结点分配失败 */
{ printf("Allocation failure\n");
exit(0); /* 结束程序运行 */
}
#include <stdlib.h>
#include <s ri g.h>
#define NEW (struct node *)malloc(sizeof(struct node))
struct ode
{ char name[20],tel[9];
struct node *next;
};
9.1 结构体
9.1.5 结构体的应用 —— 链表
main( )
{ struct node *head;
……
head=create( );
……
}
2009-8-17
41
【 例 9.14】 输出学生电话簿链表函数 。
9.1 结构体
9.1.5 结构体的应用 —— 链表
4,输出单向链表中各结点信息
h
p
Chang
62783410
Li
68752341
NULL
Wang
63212986
p指向第一个结点 p=head
当 p不为 NULL
输出结点数据
p指向下一个结点
p=p->next
图 9.5 输出链表的 N-S图
p p p
NULL
2009-8-17
42
void prlist(struct node *head)
{ struct node *p;
p=head;
while (p!=NULL)
{ printf("%s\t%s\n",p->name,p->tel);
p=p->next;
}
}
#include <stdlib.h>
#include <string.h>
#define NEW (struct node *)malloc(sizeof(struct node))
struct node
{ char ame[20],tel[9];
struct node *next;
};
9.1 结构体
9.1.5 结构体的应用 —— 链表
main( )
{ struct node *head;
……
head=create( );
prlist(head);
……
}
2009-8-17
43
在链表中,如果要删除第 i个结点,一般是将第( i-1)
个结点直接与第( i+1) 个结点相连接,然后再释放第 i个结点的存储单元 。
9.1 结构体
9.1.5 结构体的应用 —— 链表
5,删除单向链表中指定的结点
h
NULL
第 i-1个结点 第 i个结点 第 i+1个结点
2009-8-17
44
【例 9.15】删除学生电话簿链表中指定学生的信息。
9.1 结构体
9.1.5 结构体的应用 —— 链表
p=head
while(strcmp(x,p->name)!=0 && p->next!=NULL)
q指针跟随 p指针后移查找 (q=p;p=p->next;)
strcmp(x,p->name)==0T F
p==headT F
head=p->next q->next=p->next 没找到
free(p)
图 9.9 删除链表中指定结点的 N-S图删除第一个结点删除中间结点或尾结点删除结点工作分两步:
查找结点删除结点学生姓名当姓名不同并且不是尾结点循环
2009-8-17
45
【例 9.15】删除学生电话簿链表中指定学生的信息。
9.1 结构体
9.1.5 结构体的应用 —— 链表
h
p
Chang
62783410
Li
68752341
NULL
Wang
63212986
(a) 删除 第一个结点 (head=p->next)
2009-8-17
46
【例 9.15】删除学生电话簿链表中指定学生的信息。
9.1 结构体
9.1.5 结构体的应用 —— 链表
h
p
Chang
62783410
Li
68752341
NULL
Wang
63212986
(b) 删除 中间结点或尾结点 (q->next=p->next)
p
q
2009-8-17
47
【例 9.15】删除学生电话簿链表中指定学生的信息。
9.1 结构体
9.1.5 结构体的应用 —— 链表
h
p
Chang
62783410
Li
68752341
NULL
Wang
63212986
p p
(c) 未找到指定的结点 (strcmp(x,p->name)!=0)
q
q
2009-8-17
48
if (strcmp(x,p->name)==0)
{ if (p==head)
head=p->next; /* 删除头结点 */
else
q->next=p->next; /* 删除中间或尾结点 */
free(p); /* 释放被删除的结点 */
}
else
printf("Not found."); /* 未找到指定的结点 */
h=head;
return h;
}
#include <stdlib.h>
#include <string.h>
#defin NEW (struct node *)malloc(sizeof(struct node))
struct node
{ char name[20],tel[9]
struct node *next;
};
9.1 结构体
9.1.5 结构体的应用 —— 链表
struct node *delnode(struct node *head,char *x)
{ struct nod *p,*q;
static struct node *h;
if (head==NULL)
{ pri tf("This is a empty list."); /* 空链表情况 */
eturn head;
}
p=head;
while (strcmp(x,p->name)!=0 && p->next!=NULL)
{ q=p;p=p->next;} /* q指针尾随 p指针向表尾移动 */
查找结点
2009-8-17
49
将一个新结点插入到链表中,首先要寻找插入的位置。
如果要求在第 i个结点前插入,可设置三个工作指针 p0,p
和 q,p0是指向待插入结点的指针。利用 p和 q指针查 找第
i个结点,找到后再将新结点链接到链表上。
9.1 结构体
9.1.5 结构体的应用 —— 链表
6,在 单向链表中 插入 结点
h
NULL
第 i个结点
p pq q
p0
p
新的第 i个结点
2009-8-17
50
head==NULLT F
p=head
head=p0 while(strcmp(x,p->name)!=0 && p->next!=NULL)
p0->next q指针跟随 p指针后移查找 (q=p;p=p->next;)
=NULL strcmp(x,p->name)==0
T F
p==headT F p->next=p0
head=p0 q->next=p0 p0->next=NULL
p0->next=p
图 9.11 在链表指定位置前插入结点的 N-S图
【例 9.16】在学生电话簿链表中插入一个学生的信息。要求将新的信息插入在指定学生信息之前,如果未找到指定学生,则追加在链表尾部。
9.1 结构体
9.1.5 结构体的应用 —— 链表当姓名不同并且不是尾结点循环空表时插入结点在表尾追加结点插入结点工作分两步:
查找插入位置连接新结点在表头插入结点在表中间插入结点
2009-8-17
51
【 例 9.16】 在学生电话簿链表中插入一个学生的信息 。 要求将新的信息插入在指定学生信息之前,如果未找到指定学生,则追加在链表尾部 。
9.1 结构体
9.1.5 结构体的应用 —— 链表
h
p
Chang
62783410
Li
68752341
NULL
Wang
63212986
(a) 在 表头 插入结点 (head=p0; p0->next=p)
Zhao
62758421
p0
2009-8-17
52
【 例 9.16】 在学生电话簿链表中插入一个学生的信息 。 要求将新的信息插入在指定学生信息之前,如果未找到指定学生,则追加在链表尾部 。
9.1 结构体
9.1.5 结构体的应用 —— 链表
h
Chang
62783410
Li
68752341
NULL
Wang
63212986
(b) 在表中间插入结点 (q->next=p0; p0->next=p)
p
q
Zhao
62758421
p0
2009-8-17
53
【 例 9.16】 在学生电话簿链表中插入一个学生的信息 。 要求将新的信息插入在指定学生信息之前,如果未找到指定学生,则追加在链表尾部 。
9.1 结构体
9.1.5 结构体的应用 —— 链表
h
p
Chang
62783410
Li
68752341
NULL
Wang
63212986
p p
(c) 在表尾追加结点 (p->next=p0; p0->next=NULL)
q
q
Zhao
62758421
p0
NULL
2009-8-17
54
if (strcmp(x,p->name)==0)
{ if (p==head)
head=p0; /* 在表头插入结点 */
else
q->next=p0; /* 在表中间插入结点 */
p0->next=p;
}
else
{ p->next=p0; /* 在表尾插入结点 */
p0->next=NULL;
}
}
h=head;
return h;
}
struct node *insert(struct node *head,struct node *p0,
char *x)
{ struct node *p,*q;
static struct node *h;
if (head==NULL)
{ head=p0; /* 空表时,插入结点 */
p0->next=NULL;
}
else
{ p=head;
while (strcmp(x,p->name)!=0 && p->next!=NULL)
{ q=p;p=q->next; }
查找插入点
9.1 结构体
9.1.5 结构体的应用 —— 链表
#include <stdlib.h>
#include <string.h>
#define NEW (struct node *)malloc(sizeof(struct node))
struct node
{ char name[20],tel[9];
struct node *next;
};
2009-8-17
559.1 结构体
9.1.5 结构体的应用 —— 链表
【 例 9.17】 学生电话簿链表管理程序 。
编制此程序可利用例 9.13至例 9.16的 4个函数完成链表的建立,输出,删除和插入等功能,这里只需编制一个
main函数完成对这 4个函数的调用 。
#include <stdlib.h>
#define NEW (struct node *)malloc(sizeof(struct node))
struct node
{ char name[20],tel[9];
struct node *next;
};
2009-8-17
569.1 结构体
9.1.5 结构体的应用 —— 链表
main( )
{ struct node *create( ),*delnode(struct node *,char *);
struct node *insert(struct node *,struct node *,char *);
void prlist(struct node *);
struct node *head=NULL,*stu;
char s[80],name[20];
int c;
2009-8-17
579.1 结构体
9.1.5 结构体的应用 —— 链表
do
{ do
{ printf("\n * * * * MENU * * * * \n");
printf(" 1,Create a list \n");
printf(" 2,Print a list \n");
printf(" 3,Delete a node \n");
printf(" 4,Insert a node \n");
printf(" 0,Quit \n");
printf(" Enter your choice(0-4),");
gets(s);
c=atoi(s);
}while(c<0 || c>4);
可以先选择 1建立一个链表,然后根据需要选择功能 2、功能 3、
功能 4、直到选择 0退出程序的运行
2009-8-17
589.1 结构体
9.1.5 结构体的应用 —— 链表
switch(c)
{ case 1,head=create( ); break;
case 2,prlist(head); break;
case 3,printf("\nInput a name deleted:\n");
gets(name); head=delnode(head,name); break;
case 4,stu=NEW; printf("\nInput a new node\n");
printf("name,"); gets(stu->name);
printf("tel,"); gets(stu->tel);
stu->next=NULL;
printf("\nInsert position\n");
printf("name,"); gets(name);
head=insert(head,stu,name);
}
}while (c);
}
2009-8-17
59
结构体类型解决了如何描述一个逻辑上相关,但数据类型不同的一组分量的集合 。
在需要节省内存储空间时,c语言还提供了一种由若干个不同类型的数据项组成,但 共享同一存储空间 的构造类型 。
9.2 共用体
9.2.1 共用体与共用体类型的定义共用体 ——一种构造类型数据共用体 由若干不同类型的数据项组成,
构成共用体的各个数据项称为 共用体成员 。
由于共享的特性,
只有最新 存储的数据是有效的。
2009-8-17
60
union 共用 体名
{ 数据类型 1 成员名 1;
数据类型 2 成员名 2;
……
数据类型 n 成员名 n;
};
9.2 共用体
9.2.1 共用体与共用体类型的定义共用体类型定义的一般形式:
union为关键字;
共用体名 是用户定义的 类型标识 。
{ }中是组成该共用体的 成员 。 成员的 数据类型 可以是 C语言所允许的任何数据类型 。
2009-8-17
61
例如:
union utype
{ int i;
char ch;
long l;
char c[4];
};
9.2 共用体
9.2.1 共用体与共用体类型的定义定义了一个 union utype共用体类型,共用体类型定义不分配内存空间,只是说明此类型数据的组成情况。
2009-8-17
62
u1变量
chil
9.2 共用体
9.2.2 共用体变量的定义与初始化
1,共用体变量的定义
利用已 定义的共用体类型名定义变量
union 共用体名 变量名表;
例如:
union utype u1,u2;
按照共用体类型的组成,系统为定义的共用体变量分配内存单元 。 共用体变量 所占内存 大小等于共用体中占用内存的 长度最长的成员 。
c
c[0]
c[1]
c[2]
c[3]
2009-8-17
639.2 共用体
9.2.2 共用体变量的定义与初始化
1,共用体变量的定义
在定义共用体类型的同时定义变量例如:
union utype
{ int i;
char ch;
long l;
char c[4];
}a,b,c;
union 共用体名
{ 成员定义表;
}变量名表;
2009-8-17
649.2 共用体
9.2.2 共用体变量的定义与初始化
1,共用体变量的定义
直接定义共用体类型变量例如:
union
{ int i;
char ch;
long l;
char c[4];
} a,b,c;
union
{ 成员定义表;
}变量名表;
2009-8-17
659.2 共用体
9.2.2 共用体变量的定义与初始化
2,共用体变量的运算
用 sizeof运算符计算共用体变量所占内存空间
union utype
{ int i;
char ch;
long l;
char c[4];
}a,b,c;
sizeof(a) 的结果为 4
sizeof(union utype) 的结果为 4
2009-8-17
669.2 共用体
9.2.2 共用体变量的定义与初始化
2,共用体变量的运算
同类型共用体变量之间的 赋值运算共用体变量之间进行赋值时,系统仅赋当前有效成员的值 (即最新存储的数据) 。
union utype
{ int i;
char ch;
long l;
char c[4];
}a,*p=&a;
对共用体变量进行 取址运算
2009-8-17
679.2 共用体
9.2.2 共用体变量的定义与初始化
3,共用体变量成员的引用共用体变量成员的引用有三种形式 。
例如:
union u
{ char u1;
int u2;
}x,*p=&x;
用共用体变量名的引用形式:
x.u1 x.u2
用共用体指针变量的引用形式,
(*p).u1 (*p).u2
p->u1 p->u2
2009-8-17
689.2 共用体
9.2.2 共用体变量的定义与初始化
4,共用体变量赋初值
【 例 9.18】 共用体变量赋初值 。
union u
{ char u1;
int u2;
};
main( )
{ union u a={0x9741};
printf("1,%c %x\n",a.u1,a.u2);
a.u1='a';
printf("2,%c %x\n",a.u1,a.u2);
}
共用体类型变量在定义时只能对第一个成员进行赋初值。
由于第一个成员是字符型,用一个字节,所以对于初值 0x9741仅能接受 0x41,
初值的高字节被截去。
1,A 41
2,a 61
对 u2成员的引用是无意义的
2009-8-17
699.2 共用体
9.2.2 共用体变量的定义与初始化
main( )
{ union
{ long n;
int k;
char c;
}un;
un.n=0x12345678;
printf("%lx\n",un.n);
printf("%x\n",un.k);
printf("%x\n",un.c);
un.c='A';
printf("%ld\n",un.n);
printf("%d\n",un.k);
printf("%c\n",un.c);
}
12345678
5678
78
305419841
22081
A
0x78
0x56
0x34
0x12
低地址高地址
0x12 0x34 0x56 0x41
0001 0010 0011 0100 0101 0110 0100 0001
高地址 低地址
2009-8-17
70
enum是关键字 ;
枚举名和枚举常量是标识符 ;
枚举常量之间用逗号分隔 。例如,enum weekday
{ Sun,Mon,Tue,Wed,Thu,Fri,Sat };
enum color1
{ blue,green,red };
enum flag
{ false,true };
9.3 枚举类型与类型命名
9.3.1 枚举类型
1,枚举类型的定义枚举类型定义的一般形式:
enum 枚举名
{ 枚举常量取值表 };
枚举 是一个具有有限个 整型符号常量的集合,这些整型符号常量称为枚举常量。
每个枚举类型都必须进行类型的定义,
定义时必须将其所有的枚举常量一一列举,
以便限定此枚举类型变量的取值范围。
2009-8-17
71
在枚举类型中,每个 枚举常量 都代表一个整型值。在定义枚举类型的同时可隐式或显式地 定义枚举常量所代表的值 。例如:
enum weekday
{ Sun,Mon,Tue,Wed,Thu,Fri,Sat };
9.3 枚举类型与类型命名
9.3.1 枚举类型
2,枚举常量的整型值隐式定义:
按照类型定义时枚举常量列举的顺序分别代表 0,1,2,…… 等整型值。
0 1 2 3 4 5 6
2009-8-17
72
例如:
enum op
{ plus=43,minus=45,multiply=42,divide=47 };
enum workday
{ Mon=1,Tue,Wed,Thu,Fri };
9.3 枚举类型与类型命名
9.3.1 枚举类型
2,枚举常量的整型值显式定义:
在定义类型的同时指定枚举常量的值,其中如有未指定值的枚举常量,则根据前面的枚举常量的值依次递增 1。
2 3 4 5
2009-8-17
73
例如:
enum flag fg;
enum color1 c1;
enum color2 { blank,brown,yellow,white }c2;
enum { lightblue,lightgreen,lightred }c3;
9.3 枚举类型与类型命名
9.3.1 枚举类型
3,枚举变量的定义枚举类型变量定义的三种形式:
① enum 枚举名 枚举变量名表 ;
② enum 枚举名 {枚举常量取值表 }枚举变量表 ;
③ enum {枚举常量取值表 }枚举变量表 ;
2009-8-17
74
⑵ 赋值运算
fg=true;c1=red;c2=yellow;c3=lightblue;
c3=white;
9.3 枚举类型与类型命名
9.3.1 枚举类型
4,枚举数据的运算
⑴ 用 sizeof运算符计算枚举变量所占内存空间枚举变量中存放的是 整型值,每个枚举变量占用 2个字节

是 enum color2
类型的枚举值
2009-8-17
75
⑷ 取址运算
enum color2 { blank,brown,yellow,white }c2;
enum flag fg;
&c2,&fg
9.3 枚举类型与类型命名
9.3.1 枚举类型
4,枚举数据的运算
⑶ 关系运算
true>false
Sun>Sat
真 ( 1)
假 ( 0)按枚举类型数据所代表的 整型值 进行比较
2009-8-17
76
在 C系统中,不能直接 对枚举数据进行 输入和输出 。
由于枚举变量可以作为整型变量处理,所以可以通过间接方法输入输出枚举变量的值。
⑵ 枚举变量的输出方法一:直接输出枚举变量中存放的整型值 。
9.3 枚举类型与类型命名
9.3.1 枚举类型
5,枚举数据的输入输出
⑴ 枚举变量的输入枚举变量作为整型变量进行输入 。 例如:
scanf("%d",&fg);
1
含义不直观
fg=true;
printf("%d",fg);
2009-8-17
77
switch(fg)
{ case false,printf("false"); break;
case true,printf("true");
}
9.3 枚举类型与类型命名
9.3.1 枚举类型
5,枚举数据的输入输出
⑵ 枚举变量的输出方法二:利用多分支选择语句输出枚举常量对应的字符串。
2009-8-17
78
enum flag
{ false,true }fg;
char *name[ ]={"false","true"};
……
fg=true;
printf("%s",name[fg]);
9.3 枚举类型与类型命名
9.3.1 枚举类型
5,枚举数据的输入输出
⑵ 枚举变量的输出方法三:依据枚举值,运用指针方法输出对应的字符串。
2009-8-17
79
fg=true;
printf("%s",fg);
9.3 枚举类型与类型命名
9.3.1 枚举类型
5,枚举数据的输入输出
⑵ 枚举变量的输出枚举常量是标识符,不是字符串,以输出字符串方式输出枚举常量是错误的。

2009-8-17
809.3 枚举类型与类型命名
9.3.1 枚举类型
【 例 9.20】 编制一个程序 。 当输入今天的星期序号后,
输出明天是星期几 。
enum weekday
{ Mon=1,Tue,Wed,Thu,Fri,Sat,Sun };
char *name[8]={"error","Mon","Tue","Wed",
"Thu","Fri","Sat","Sun"};
2009-8-17
819.3 枚举类型与类型命名
9.3.1 枚举类型
main( )
{ enum weekday d;
printf("Input today’s numeral(1-7):");
scanf("%d",&d);
if (d>0&&d<7) d++; /* 今天是星期一到星期六的时候 */
else if (d==7) d=1; /* 今天是星期日 */
else d=0;
if (d)
printf("Tomorrow is %s.\n",name[d]);
else
printf("%s\n",name[d]);
}
2009-8-17
829.3 枚举类型与类型命名
9.3.2 类型的重新命名
1,为类型名定义别名为类型名定义别名的一般形式,
typedef 类型名 新类型名或
typedef 类型定义 新类型名用 typedef为已存在的类型名再命名一个新的类型名(即别名)。
typedef是关键字;
类型名 可以是 基本类型,构造类型,指针类型或自定义类型名;
新类型名 是自定义的类型名 。
2009-8-17
839.3 枚举类型与类型命名
9.3.2 类型的重新命名
typedef int COUNTER;
/* 定义 COUNTER为整型类型名 */
typedef struct date
{ int year;
int month;
int day;
}DATE; /* 定义 DATE为 struct date结构体类型名 */
新类型名与旧类型名作用相同,并且可同时使用。
int i; 与 COUNTER i; 等价。
struct date birthday; 与 DATE birthday; 等价。
2009-8-17
849.3 枚举类型与类型命名
9.3.2 类型的重新命名
2,为类型命名的方法
⑴ 为基本类型命名例如:
typedef float REAL;
REAL x,y; /* 相当于 float x,y; */
以 typedef开头,加上变量定义的形式,
并用新类型名替代变量名。
⑵ 为数组类型命名例如:
typedef char CHARR[80];
CHARR c,d[4];
/* 相当于 char c[80],d[4][80]; */
2009-8-17
859.3 枚举类型与类型命名
9.3.2 类型的重新命名
2,为类型命名的方法
⑶ 为指针类型命名例如:
typedef int *IPOINT;
IPOINT ip; /* 相当于 int *ip; */
IPOINT *pp; /* 相当于 int **pp; */
typedef int (*FUNpoint)( );
FUNpoint funp; /* 相当于 int (*funp)( ); */
2009-8-17
869.3 枚举类型与类型命名
9.3.2 类型的重新命名
2,为类型命名的方法
⑷ 为结构体,共用体类型命名例如:
struct node
{ char c;
struct node *next;
};
typedef struct node CHNODE;
CHNODE *p; /* 相当于 struct node *p; */
struct CHNODE *p;〤