C语言程序设计
2002 年第 八 章 结构与联合第八章 结构与联合
C的构造类型:
( 1) 数组,( 2) 结构,( 3) 联合 。
在实际事务处理中,一些对象有多个属性,如学生成绩档案处理,学生是被处理的基本对象,有多个属性:
(a) 学号(长整数或字符串),(b) 姓名(字符串),
(c) 性别(枚举常量或字符),(d)年龄(整数或短整数)
(e) 各科成绩(单精度浮点数数组),……
表示这些属性的数据构成一个学生的数据记录。由于数据记录的每个数据成员的类型不相同,不能用数组表示,但可以用一个 结构变量 来表示,全班学生则用一个结构数组表示。
结构变量 来实现将多个不一定相同类型的数据集成在一起表示一个数据对象,其各个成员代表数据对象的各个属性。
8.1 结构的说明的引用
8.1.1 结构的说明结构说明任务就是定义结构的类型和用定义的结构类型来说明结构变量。
一、结构类型的定义形式存储类型区分符 struct 结构名 {结构成员说明表 };
例,struct stud{ /*定义一个能表示学生属性的结构类型 */
long num;
char name[10];
char sex;
short age;
int score[4]; /*四门课程的成绩 */
};
二、结构类型的变量说明
( A) 先定义类型,后说明变量:
struct stud student1,student2;
( B) 定义类型的同时说明变量:
struct stud{ /*定义一个能表示学生属性的结构类型 */
long num;
char name[10];
char sex;
short age;
int score[4]; /*四门课程的成绩 */
} student1,student2;
( C)定义匿名结构类型的同时说明变量,该类型仅能使用一次:
struct { /*定义一个能表示学生属性的结构类型 */
long num;
char name[10];
char sex;
short age;
int score[4]; /*四门课程的成绩 */
} student1,student2;
三、结构变量的初始化
struct stud {
long num;
char name[10];
char sex;
short age;
int score[4]; /*四门课程的成绩 */
} student1={98183341;“zhang san”,?m?,18,{80,90,78,86}};
struct stud student2={98183342;“Li Si”,?m?,18,{85,91,88,87}};
注意,初值表中各初始化表达式的类型应与定义成员的类型一一对应。
8.1.2 结构的引用一、引用结构变量
( 1) 同类型的结构变量可以相互赋值。
例:
struct stud s1= {98183341,“zhang san”,?m?,18,{80,90,78,86}};,
struct stud s2=={98183342,“Li Si”,?m?,18,{85,91,88,87}},s3;
s3=s1;s1=s2;s2=s3;
注,s1= {98183341;“zhang san”,?m?,18,{80,90,78,86}};非法语句
( 2) 结构变量可以取地址。
结构变量也分配连续存储单元,例:
struct stud student;
num 4字节
name 10字节
sex(1) 1 字节
age 2 字节
score 4*4 字节
student
&student
&student.num
student.name
&student.sex
&student.age
student.score
&student.score[0]
二、成员选择运算符‘,?及结构成员的引用形式,结构变量名,成员名
struct stud s1;
s1.num= 98183341; scanf(“%ld”,&s1.num);
strcpy(s1.name,“zhang san”); scanf(“%s”,s1.name);
s1.sex=?m?; scanf(“%c”,&s1.sex);
s1.age=18; scanf(“%hd”,&s1.age);
s1.score[0]=80; scanf(“%d”,&s1.score[0]);
s1.score[1]=87; scanf(“%d”,&s1.score[1]);
s1.score[2]=90; scanf(“%d”,&s1.score[2]);
s1.score[3]=95; scanf(“%d”,&s1.score[3]);
二、嵌套的结构结构成员可以是结构,形成结构的嵌套形式。
strcut date{ char month_name[4];
int day;
int year;
}
struct nstud{
long num;
char name[10];
char sex;
struct date birthday;
int score[4]; }
struct nstud{
long num;
char name[10];
char sex;
strcut date{ char month_name[4];
int day;
int year;
} birthday;
int score[4]; }
struct nstud stu1,stu2;
strcpy(stu1.birthday.month_name,”Feb”);
stu1.birthday.day=16; stu1.birthday.year=1980;
8.2 结构变量的指针一、结构指针的说明存储类型区分符 struct 结构类型名 *标识符;
例,struct stud s1,s2 *pstu;
pstu=&s1;
二、用结构指针引用结构变量成员
(*pstu).num=98183341;
scanf(“%s”,(*pstu).name);
for(i=0;i<4;i++)
printf(“%d”,(*pstu).score[i]);
注意,(*结构指针名 ).成员名 中的 ()不能省略。,.”的运算符优先级高于‘ *’。
三、成员选择运算符,->”
形式:结构的指针 ->成员名例,struct date d,*p=&d;
下列同一行上的三个表达式等价:
p->year d.year (*p).year
p->day d.day (*p).day
p->month_name d,month_name (*p),month_name
“->”的优先级等同与,.”。
struct nstud s,*p=&s;
s.birthday.year (*p).birthday.year
p->birthday.year (p->birthday).year
四个表达式等价
struct A {int len;
char *str;} s={1,”0123456”},*p=&s;
表达式 表达式的值 表达式执行的操作
++p->len 2 len增 1后访问 len
p->len++ 1 访问 len后 len增 1
*p->str 48 str所指对象‘ 0?的值
*p->str++ 48 访问 str所指对象‘ 0?
的值后 str增 1指向‘ 1?
(*p->str)++ 48 访问 str所指对象‘ 0?
后 str所指对象‘ 0?增 1
*++p->str 49 单目运算右结合,
先 str增 1,再 str所指对象‘ 1?的值
len 1
str
p
0?
1?
2?
3?
4?
5?
6?
\0?
s
8.3 结构和函数一、结构和结构指针作为函数参数将结构传递给函数有三种可用的方法:
( 1)将每一个结构成员作为一个参数;
一般仅当需要结构变量的个别成员值时使用。
( 2)将整个结构变量作为一个参数;
函数对结构变量的副本进行操作。
( 3)用指向结构变量的指针作参数。
对较大的结构变量传递效率较高,仅传递一个指针的值。
同时被调用函数可对调用函数中结构变量的值进行修改。
二、结构和结构指针作为函数返回值
( 1)返回结构的函数说明形式:
存储类型区分符 struct 类型区分符 函数名(参数表)
( 2)返回结构指针的函数说明形式:
存储类型区分符 struct 类型区分符 *函数名(参数表)
三、程序举例首先定义一表示复数的结构类型:
/*comp.h*/
struct complex {
float re; /*实部 */
float im; /*虚部 */
}
例 1:编写一函数 makecomp,生成一复数数据
#include,comp.h”
struct complex makecomp(float x,float y)
{ struct complex z;
z.re=x; z.im=y;
return z;
}
main()
{ struct complex comp1,comp2; float x1,x2,y1,y2;
scanf(“%f%f%f%f”,&x1,&y1,&x2,&y2);
comp1=makecomp(x1,y1);
comp2=makecomp(x2,y2);
}
例 2:编写一函数 addcomp,计算两复数之和。
#include,comp.h”
struct complex *addcomp(struct complex x,struct complex y)
{ static struct complex z;
z.re=x.re+y.re; z.im=x.im+y.im;
return &z;
}
main()
{ struct complex comp1={1.0,2.0},comp2={3.0,4.0},*p;
p=addcomp(comp1,comp2);
printf(“result,re=%.2f \t im=%.2f\n”,p->re,p->im);
}
例 2:编写一函数 addcomp,计算两复数之和。
#include,comp.h”
struct complex *addcomp(struct complex *x,struct complex *y)
{ static struct complex z;
z.re=x->re+y->re; z.im=x->im+y->im;
return &z;
}
main()
{ struct complex comp1={1.0,2.0},comp2={3.0,4.0},*p;
p=addcomp(&comp1,&comp2);
printf(“result,re=%.2f \t im=%.2f\n”,p->re,p->im);
}
8.4 结构数组结构数组是以同类型的结构变量为元素的数组。可用于学生登记表、职工登记表、图书目录商品销售清单、库存清单等二维表格数据。
一、结构数组的说明、引用和初始化例:说明一个具有 30个元素的学生结构数组。
struct stud{ /*定义一个能表示学生属性的结构类型 */
long num;
char name[10];
char sex;
short age;
float score;
} students[30];
结构数组逻辑上表示一个二维表格:
学生情况登记表
num name sex age score
student[0] 2129500 张三 m 18 83.5
student[1] 2129501 李四 m 19 90.5
student[2] 2129502 王红 f 18 85.
… … … … … …
student[29] 2129529 程萍 f 19 87.
表中每一行表示一个元素,即一个学生的记录,用结构变量形式表示;
结构数组的元素的引用与普通数组元素的形式完全一样;例如,student[i] 此处 student[i]为一结构变量,与一般结构变量用法相同。
例:说明一个具有 5个元素的学生结构数组。
struct stud students[5]={
{2129500,“张三”,‘ m?,18,83.5},
{2129501,“李四”,‘ m?,18,90.5},
{2129502,“王红”,‘ m?,18,85},
{2129503,“田亮”,‘ m?,19,87},
{2129504,“张灵”,‘ f?,18,81.5},
};
for(i=0;i<5;i++)
printf(“\n%10ld%15s%4c%6hd%8.1f”,student[i].num,
student[i].name,
student[i].sex,student[i].age,student[i].score);
for(sum1=0,sum2=0,i=0;i<5;i++)
{ sum1+= student[i].age; sum2+= student[i].score;}
printf(“\n%-29s%6.1%8.1f”,sum1/5.,sum2/5);
二、用结构的指针指向结构数组的成员。
例 1,struct stud students[30] 。 *p;
for(p=students;p<&students[30];p++)
printf(“\n%10ld%15s%4c%6hd%8.1f”,p->num,p->name,
p->sex,->age,p->score);
例 2,struct { int len;
char *str;
} a[2]={{1,”0123456”},{10,”98765”}},*p=a;
(++p)->len 值为 10,p先增 1,然后访问 p当前所指 a[1]的
len
(p++)->len 值为 1,访问 p当前所指 a[0]的 len,然后 p增 1.
*p++->str 值为 48,访问 p当前所指 a[0]的 str[0],然后 p增 1.
*(++p)->str 值为 57,p先增 1,然后访问 p当前所指 a[1]的 str[0]
注意下述表达式的区别:
(++p)->len 与 ++p->len,后者是 len增 1
(p++)->len 与 p++->len相同
,两者均为 p增 1
*p++->str 与 *(p++->str)相同,*作用于 str上,++作用于
p上
*(++p)->str 与 *++p->str不同
,后者是 str加 1
p?0?
1?
2?
3?
4?
5?
6?
\0?
a[0] len 1
str
a[1] len 10
str
9?
8?
7?
6?
5?
\0?
三、结构的大小与 sizeof运算符结构的大小 是指一个结构变量占用的存储空间的字节数,
一般等于一个结构变量所有成员的存储字节数的总和。
对于结构中长度不满一个字长的成员,例如 char,short类型的成员在 32位机中,不同编译在实际分配存储单元时的处理方法可能不同:
( 1)一种是使成员的地址在 字边界 对齐,于是结构变量中留下一个或多个不用的空白空间,这种情况下,结构的大小大于所有成员的存储字节数的总和 。
( 2)另一种是使成员的地址在 字节边界 对齐,这种情况下,
结构的大小 等于所有成员的存储字节数的总和 。
基于上述原因,为提高程序的可移植性,当需要使用结构的大小时,不能有人工计算,应使用 sizeof运算符来计算。
一般用法:
sizeof (对象名) 如变量名,常量名等
sizeof (类型名)
例:
float x; const int YES=1;
struct stud student,students[30];
sizeof( char) 1,sizeof(short) 2,sizeof(float) 4
sizeof (x) 4,sizeof(YES) 2
sizeof (struct stud) 15
sizeof (student) 15
sizeof (students) (21或 22) *30
四、结构数组作函数参数结构数组作函数参数时,形式参数为数组元素结构类型的指针,实参可以是结构数组名或结构数组第 0个元素的地址。
例:根据学生的成绩按从高到低排序。
#include,stdio.h”
define NSTUDENTS (sizeof( students)/sizeof (struct stud)) /*学生人数 */
struct stud{ long num;
char name[10];
char sex;
short age;
float score;
} students[]={
{2129500,“张三”,‘ m?,18,-1},{2129501,“李四”,‘ m?,18,
-1},
{2129502,“王红”,‘ m?,18,-1},{2129503,“田亮”,‘ m?,19,
void readscore(struct stud *,int);
void sortbyscore(struct stud *,int);
void writescore(struct stud *,int);
void main()
{
printf(“input %d scores:\n”,NSTUDENTS);
readscore(students,NSTUDENTS);
sortbyscore(students,NSTUDENTS);
printf(“num\tname\tsex\tage\score\n”);
writescore(students,NSTUDENTS);
return 0;
}
void readscore(struct stud *stab,int numbers)
/*输入 numbers学生的成绩 */
{
int i;
float temp;
for(i=0;i<numbers;i++)
{
scanf(“%f”,&temp);
stab[i].score=temp;
}
}
void sortbyscore(struct stud *stab,int numbers);
/*根据成绩递减排序,选择排序法 */
{ int i,j;
struct temp;
for (i=0;i<numbers-1;i++)
for(j=i+1;j<numbers;j++)
if (stab[i].score< stab[j].score)
{ temp=stab[i];
stab[i]=stab[j];
stab[j]=temp;
}
}
void writescore(struct stud *stab,int numbers)/*输出成绩清单 */
{ int i;
for (i=0;i<numbers;i++)
printf(“%-ld\t%-s\t%-c\t%-d\t%-0.2f\n”,
stab[i].num,stab[i].name,stab[i].sex,
stab[i].age,stab[i].score);}
输出,input 4 score:
输入,84 69 91 88
输出,num name sex age score
2129502 王红 m 18 91.00
2129503 田亮 m 19 88.00
2129500 张三 m 18 84.00
2129501 李四 m 18 69.00
8.5 结构和指针的应用
8.5.1 动态数据结构
数据结构是一种类型的数据在机内的存储结构,目前为止所接触到的都是静态数据结构,静态数据结构通过变量说明建立,变量所占用的存储器空间的大小是在变量说明时确定的,
程序执行过程中不能改变。
动态数据结构是在程序运行过程中通过调用系统提供的动态分配函数根据程序要求分配连续存储空间,没有相应的变量名,一般用指针指向这连续存储空间。
动态数据结构适应数据成员个数不固定,或需要经常增加或减少的数据,
例如,struct stud students[50];
结构数组表示一个班的学生记录,不能很好的满足各种情况。
动态数据结构可解决此类问题,根据实际情况分配记录空间。
data next
stud1
head data next
stud2
data next
studn null……
这种通过一个指针将一个数据元素和另一个数据元素相连的数据存储形式称为 单向链表 。
head
stud1
nextdata
stud2
nextdata
studn
nextdata
……
单向循环链表 。
null
front data behide
stud1
head
……
front data behide
stud2
front data behide
nullstudn
front data behidehead front data behide
stud2
front data behide
studn……
双向链表双向循环链表
stud1
动态数据结构的元素称为,结点,。每个结点是类型相同的一个结构变量。与数组不同,每个结点的存储空间不一定相邻。
结点由两部分组成,
( 1) 数据域,存放被处理的任何类型数据;
( 2) 指针域,存放下一结点的地址(指针),指针的类型为结点的类型,即为指向自身的结构指针。
例如:单链表结点定义:
struct stud_node
{ struct stud data; /*数据域,存放一个学生记录 */
struct stud_node *next; /*指针域,指向下一学生记录
*/
}
8.5.2 C的动态存储分配函数
( 1) void *malloc(size_t size)
功能:向系统申请 size字节的存储空间。分配成功时,返回分配的连续存储空间的首地址;分配失败则返回 NULL。
新分配的存储区未被初始化。
例,#define SIZE 10
int i,*p;
p=malloc(SIZE*sizeof(int)); /*分配 10个整型数的存储空间 */
if (p==NULL) {printf(“分配失败 !”); exit(0);}
for(i=0;i<SIZE;i++)
scanf(“%d”,&p[i]);
( 2) void *calloc(size_t nobj,size_t size)
功能:向系统申请有 nobj个元素的数组存储空间,每个元素的大小为 size字节的存储空间,共向系统申请 nobj× size 个字节的存储空间。分配成功时,返回分配的连续存储空间的首地址;分配失败则返回 NULL。
新分配的存储区均被初始化为 0。
例,int i,num; struct stud *p
printf(“输入班级人数:” );
scanf(“%d”,&num); /*分配存储 num个学生记录的存储空间 */
p=calloc(NUMBERS,sizeof(struct stud));
if (p==NULL) {printf(“分配失败 !”); exit(0);}
for(i=0;i<num;i++)
{ /*依次输入学生记录信息 */ }
( 3) void *realloc(void *p size_t size)
功能:向系统重新申请有大小为 size字节的存储空间,p指向以由 malloc和 calloc分配的存储空间。分配成功时,返回分配的连续存储空间的首地址;分配失败则返回 NULL。
例,struct stud *p1,*p2;
p1=malloc(30*sizeof(struct stud)); /*分配 30个学生记录空间 */
if (p1==NULL) {printf(“分配失败 !”); exit(0);}
for(i=0;i<num;i++)
{ /*依次输入学生记录信息 */ }
p2=malloc(p1,50*sizeof(struct stud)); /*分配 50个学生记录空间 */
if (p2==NULL) {printf(“重新分配失败 !”); exit(0);}
田亮 …第 30个学生记录第 29个学生记录
…
…
…
…
…
王红 …第 3个学生记录李四 …第 2个学生记录张三 …第 1个学生记录
p1
第 50个学生记录
…
…
…
…
第 30个学生记录第 29个学生记录
…
…
第 3个学生记录第 2个学生记录第 1个学生记录
p2
张三 …
李四 …
王红 …
…
…
…
田亮 …
( 4) void free(void *p)
功能,释放向系统重新申请存储空间,p指向以由 malloc、
calloc和 realloc分配的存储空间。
注意,动态分配的存储空间一定要用 free函数释放,而静态分配的存储空间当程序运行完成的时候自动释放。
例,struct stud *p1,*p2;
p1=malloc(30*sizeof(struct stud)); /*分配 30个学生记录空间 */
for(i=0;i<num;i++)
{ /*依次输入学生记录信息 */ }
p2=malloc(p1,50*sizeof(struct stud)); /*分配 50个学生记录空间 */
free(p1); ……
free(p2);
stud1
nextdatahead
stud2
nextdata
nullstudn
nextdata
……
8.5.3 链表头指针 链头 链尾说明:
用头指针表示链表;
head->data; 第一个学生的记录数据
head->data.num 第一个学生的学号
head->data.name 第一个学生的姓名
head->next; 第一个结点的指针域,第二个结点的指针
head->next->data 第二个结点的记录数据
head->next->next 第二个结点的指针域,,第三个结点的指针空链表条件,head==NULL
访问链表所有结点:
struct stud_node *p;
p=head; /*p指向第一个结点 */
while (p!=NULL) /* p指向的结点存在 */
{
printf(“%s\n”,p->data.name); … /* 访问 p指向结点的数据 */
p=p->next; /*p指向下一个结点 */
}
一、链表的建立和输出根据插入新结点的方式不同,链表分为:
( 1),先进先出”链表:
a1
nextdatahead
a2
nextdata
nullan
nextdata
……
( 2)“先进后出”链表:
an
nextdata
an-1
nextdatahead
nulla1
nextdata
……
例:输入一批整数,以 0为结束,将它们建成一个先进后出链表
( 0不包含在链中),然后输出所有结点的值。
( 1) 结点类型定义:
struct node {
int data;
struct node *next;
}
( 2)每输入一个整数 n,
( A) 建立一个结点,有指针变量 p指向:
p=malloc(sizeof(struct node));
p->data=n;
( B) 将指针变量 p指向的结点插入链:
空链表时的插入,
head null
1
nextdata
p null
2
nextdata
head 1
nextdata
3
nextdata
p
非空链表时的插入,
p->next=head;
head=p;
p->next=head;
head=p;
#include,stdio.h”
#include,stdlib.h”
struct node { int data;
struct node *next;
}
main()
{ int n;
struct node *p,*head;
head=NULL; /*链表初始化 */
printf(“输入整数序列,以 0结束,”);
scanf(“%d”,&n);
while (n) { p=malloc(sizeof(struct node));
p->data=n;
p->next=head;
head=p;
scanf(“%d”,&n); }
p=head;
while(p) { printf(%6d”,p->data);
p=p->next; }
}
输入,1 2 3 4 5
输出,5 4 3 2 1
例:输入一批整数,以 0为结束,将它们建成一个先进先出链表
( 0不包含在链中),然后输出所有结点的值。
( 1) 结点类型定义:
struct node {
int data;
struct node *next;
}
( 2)每输入一个整数 n,
( A) 建立一个结点,有指针变量 p指向:
p=malloc(sizeof(struct node));
p->data=n;
( B) 将指针变量 p指向的结点插入链:
head
p
/// 1 2 3 4 5
p p p p
头指针 头结点
tail
head=malloc(sizeof(struct node))
tail=head
tail
p=malloc(sizeof(struct node))
tail->next=p
tail=p
p=malloc(sizeof(struct node))
tail
tail->next=p
tail=p
tail tail tail
tail->next=NULL;
0
#include,stdio.h”
#include,stdlib.h”
struct node { int data;
struct node *next;
}
main()
{ int n;
struct node *p,*head,*tail;
head=malloc(sizeof(struct node)); /*链表初始化 */
tail=head;
printf(“输入整数序列,以 0结束,”);
scanf(“%d”,&n);
while (n) { p=malloc(sizeof(struct node));
p->data=n;
tail->next=p;
tail=p;
scanf(“%d”,&n); }
tail->next=NULL; p=head->next; /*p指向首结点 */
while(p) { printf(%6d”,p->data);
p=p->next; }
}
输入,1 2 3 4 5
输出,1 2 3 4 5
///
head
12 2 31 4 10
p
二、删除 p指向的链表结点
…
q
q->next=p->next;
free(p);
int delete_node(struct node *head,int x)
{
struct node *p,*q;
q=head; p=head->next; /*q指向头结点,p指向第一个结点 */
while (p && p->data!=x)
{ q=p; p=p->next;}
if (p==NULL) return 0; /*符合条件的结点不存在 */
q->next=p->next;
free(p);
return 1; /*符合条件结点已删除 */
}
///
head
2 12 31 34 078
三、插入一个结点到按结点数据域递增排序的链表中,使其仍然递增。
…
f 28
pq pq pq
f->next=p;
q->next=f;
int insert_node(struct node *head,int x)
{ struct node *p,*q,*f;
f=malloc(sizeof(struct node));
if (f==NULL) {printf(“新结点不能建立” ); return 0;}
q=head; p=head->next; /*q指向头结点,p指向第一个结点 */
while (p && x > p->data) /*查找定位 */
{ q=p; p=p->next;}
f->next=p;
q->next=f;
return 1; /*插入成功 */
}
int main()
{ struct node *head; int n;
head=malloc(sizeof(struct node));
if (head==NULL) {printf(“链表不能建立” ); return 0;}
head->next=NULL; /*链表初始化 */
printf(“输入整数序列,以 0结束,”);
scanf(“%d”,&n);
while (n) { insert_node(head,n);
scanf(“%d”,&n); }
………… /* 其它处理 */
}
四、链表排序首先,统计出结的个数 n,再按照选择排序法进行排序,
第一趟处理完后,第一结点值最大,第一趟处理完后,第二结点值最大,依此类推。 下例中链表为结点数据为整型的 带头结点 的形式。
void sortchain(struct int node)
{ int i,temp,n=0;
struct intnode *p1,*p2;
p1=head->next; /*p1指向第一个结点 */
while (p1!=NULL) /*统计结点个数 */
{ n++;
p1=p1->next; /*p1指向下一个结点 */
}
/*i控制 n-1趟选择 */
/*p1依次指向第 1、第 2、第 3、。。。、第 n-1个结点 */
/*p2依次指向 p1后的所有个结点 */
for(p1=head->next,i=1; i<n; i++,p1=p1->next)
for(p2=p1->next; p2!=NULL; p2=p2->next)
if (p1->data<p2->data)
{ /*交换结点的值 */
temp=p2->data;
p2->data=p1->data;
p1->data=temp;
}
}
8.6 联 合前面所述的所有数据中,类型是固定的;
实际应用中,有些数据在不同场合表现为不同类型的数据;如政治面貌,在职工(学生)管理系统中是一个人的属性,或者是群众,用字符串表示(也可以干脆空白) ;或者是党(团)员,则进一步要求填写入党(团)时间,入党
(团)地点和入党(团)介绍人,此时必须用结构类型数据表示 。
联合类型可满足这一类要求。 联合类型的变量(简称联合) 由多个成员组成,这些 成员并不同时存在,而是在不同时刻拥有不同的成员,在同一时刻仅拥有其中一个成员 。
联合的成员可以是任何类型的数据,包括任何基本类型、指针、数组、结构和联合。
一,联合的说明、引用和初始化除用关键字 union代替 struct外,联合类型的定义,联合变量的说明及联合成员的应用在语法上与结构完全相同;
例如:假定一种数据对象可能是 int类型,double类型或字符类型,为了在同一个存储区域存放这样的数据对象,可说明如下联合:
union {
char ch; /*1字节 */
int i; /*2字节 */
float f ; /*4字节 */
} u,* p=&u;
u.ch=?a?; printf(“%C:%d”,u.ch,p->ch); /*输出 c,97*/
u.i=450; printf(“%d,%d”,u.i,p->i); /*输出 450,450*/
u.f=450; printf(“%02f,%.2lf”,u.f,p->f);
/*输出 450.00,450.00*/
u.ch=20;
u.i=266; printf(“%d,%d”,u.ch,u.i);
/*输出,10,266 其中,10是错误数据,因此时成员 i有效 */
u.f=450; printf(“%c,%d,%.2f”,u.ch,(*p).i,p->f);
/*输出:,0,450.00 其中,仅成员 d有效,450为正确数据 */
注意,(1)使用联合变量时,必须知道当前有效成员。
(2) 联合的大小大于或等于最长成员的字节数例:联合变量作为结构成员:
职工档案管理中,婚姻状态:
1。未婚:仅需要一标志;
2。已婚:配偶姓名,孩子数;
3。离婚:日期。
struct date { char month[4];
int day;
int year;
}
enum marristate {married,divorce,single};
struct employee {
char num[15];
char name[10];
char sex;
enum marristate state; /*婚姻状态,
union {
struct{ char sname[10];
int childnum;
} spouse;
struct date divorcedate;
} statemess;
} emp[300];
/*输入职工信息 */ printf(“…………”); /* 输出表头 */
for(i=0;i<numbers_of_employee; i++}
{ printf(“%20s%12s%s”,emp[i].num,emp[i].name;
emp[i].sex==?m? && emp[i].sex==?M,男”:“女” );
switch (emp[i].state) {
case single,printf(“未婚” ); break;
case married,printf(“已婚 %12s%4d”,
emp[i].statemess.spouse.sname,
emp[i].statemess.spouse.childnum);break;
case divorce,printf(“离婚 %s%4d%6d”,
emp[i].statemess.divorcedate.month,
emp[i].statemess.divorcedate.day,
emp[i].statemess.divorcedate.year);
} /*end of switch */
} /*end of for*/
8.7 typedef说明
typedef说明为类型定义一个名字,相当于原类型的别名。
一,typedef说明的形式
typedef 类型区分符号 说明符例 1,typedef int INTEGER; /*定义 int类型别名 */
INTEGER i,j;
例 2,typedef char *STRING; /*定义字符指针类型别名 */
STRING str1=“abc”,str2=“123”;
例 3,typedef int VECTOR[50];
/*定义 VECTOR是 int[50]别名 */
VECTOR v1,v2; /*v1,v2都是长度为 50整型的数组 */
例 4,typedef struct stud{
long num;
char name[10];
char sex;
short age;
float score;
} STUD; /*/定义结构类型 struct stud的别名 STUD*/
STUD student,*p;
例 5,typedef int FI(char *,char *)
FI是类型为,int (char *,char *)的别名,表示有两个字符指针参数,返回类型为 int的函数。
FI strcmp,strcat; /*两函数的说明 */
二,typedef说明的用途
( 1)使程序参数化,便于程序移植。
例如数据类型 char的符号问题,int的长度问题,与编译环境有关,导致程序需要进行大量修改才能正常运行,如果给其定义一个别名,程序中说明变量时使用别名,这样一旦发生环境改变,仅需修改别名定义。
例:在 32位机上:
typedef int INTEGER;
INTEGER i,j; /*i,j占 4字节 */
在 16位机上仅需修改别名定义:
typedef long INTEGER;
( 2)提高可读性,编辑效率。
例 1,typedef unsigned size_t
void *malloc(size_t size);
等给现有类型定义一个有一定意义的别名。
例 2,typedef struct stud STUD;
STUD stu1,stu2;
注意,typedef 没有定义新类型,仅仅为现有类型定义一个类型的别名。
定义别名方法:(定义一个 4*2的整型数组类型)
(a) int INT_MATRICS[4][2];
(b) typedef int INT_MATRICS[4][2];
2002 年第 八 章 结构与联合第八章 结构与联合
C的构造类型:
( 1) 数组,( 2) 结构,( 3) 联合 。
在实际事务处理中,一些对象有多个属性,如学生成绩档案处理,学生是被处理的基本对象,有多个属性:
(a) 学号(长整数或字符串),(b) 姓名(字符串),
(c) 性别(枚举常量或字符),(d)年龄(整数或短整数)
(e) 各科成绩(单精度浮点数数组),……
表示这些属性的数据构成一个学生的数据记录。由于数据记录的每个数据成员的类型不相同,不能用数组表示,但可以用一个 结构变量 来表示,全班学生则用一个结构数组表示。
结构变量 来实现将多个不一定相同类型的数据集成在一起表示一个数据对象,其各个成员代表数据对象的各个属性。
8.1 结构的说明的引用
8.1.1 结构的说明结构说明任务就是定义结构的类型和用定义的结构类型来说明结构变量。
一、结构类型的定义形式存储类型区分符 struct 结构名 {结构成员说明表 };
例,struct stud{ /*定义一个能表示学生属性的结构类型 */
long num;
char name[10];
char sex;
short age;
int score[4]; /*四门课程的成绩 */
};
二、结构类型的变量说明
( A) 先定义类型,后说明变量:
struct stud student1,student2;
( B) 定义类型的同时说明变量:
struct stud{ /*定义一个能表示学生属性的结构类型 */
long num;
char name[10];
char sex;
short age;
int score[4]; /*四门课程的成绩 */
} student1,student2;
( C)定义匿名结构类型的同时说明变量,该类型仅能使用一次:
struct { /*定义一个能表示学生属性的结构类型 */
long num;
char name[10];
char sex;
short age;
int score[4]; /*四门课程的成绩 */
} student1,student2;
三、结构变量的初始化
struct stud {
long num;
char name[10];
char sex;
short age;
int score[4]; /*四门课程的成绩 */
} student1={98183341;“zhang san”,?m?,18,{80,90,78,86}};
struct stud student2={98183342;“Li Si”,?m?,18,{85,91,88,87}};
注意,初值表中各初始化表达式的类型应与定义成员的类型一一对应。
8.1.2 结构的引用一、引用结构变量
( 1) 同类型的结构变量可以相互赋值。
例:
struct stud s1= {98183341,“zhang san”,?m?,18,{80,90,78,86}};,
struct stud s2=={98183342,“Li Si”,?m?,18,{85,91,88,87}},s3;
s3=s1;s1=s2;s2=s3;
注,s1= {98183341;“zhang san”,?m?,18,{80,90,78,86}};非法语句
( 2) 结构变量可以取地址。
结构变量也分配连续存储单元,例:
struct stud student;
num 4字节
name 10字节
sex(1) 1 字节
age 2 字节
score 4*4 字节
student
&student
&student.num
student.name
&student.sex
&student.age
student.score
&student.score[0]
二、成员选择运算符‘,?及结构成员的引用形式,结构变量名,成员名
struct stud s1;
s1.num= 98183341; scanf(“%ld”,&s1.num);
strcpy(s1.name,“zhang san”); scanf(“%s”,s1.name);
s1.sex=?m?; scanf(“%c”,&s1.sex);
s1.age=18; scanf(“%hd”,&s1.age);
s1.score[0]=80; scanf(“%d”,&s1.score[0]);
s1.score[1]=87; scanf(“%d”,&s1.score[1]);
s1.score[2]=90; scanf(“%d”,&s1.score[2]);
s1.score[3]=95; scanf(“%d”,&s1.score[3]);
二、嵌套的结构结构成员可以是结构,形成结构的嵌套形式。
strcut date{ char month_name[4];
int day;
int year;
}
struct nstud{
long num;
char name[10];
char sex;
struct date birthday;
int score[4]; }
struct nstud{
long num;
char name[10];
char sex;
strcut date{ char month_name[4];
int day;
int year;
} birthday;
int score[4]; }
struct nstud stu1,stu2;
strcpy(stu1.birthday.month_name,”Feb”);
stu1.birthday.day=16; stu1.birthday.year=1980;
8.2 结构变量的指针一、结构指针的说明存储类型区分符 struct 结构类型名 *标识符;
例,struct stud s1,s2 *pstu;
pstu=&s1;
二、用结构指针引用结构变量成员
(*pstu).num=98183341;
scanf(“%s”,(*pstu).name);
for(i=0;i<4;i++)
printf(“%d”,(*pstu).score[i]);
注意,(*结构指针名 ).成员名 中的 ()不能省略。,.”的运算符优先级高于‘ *’。
三、成员选择运算符,->”
形式:结构的指针 ->成员名例,struct date d,*p=&d;
下列同一行上的三个表达式等价:
p->year d.year (*p).year
p->day d.day (*p).day
p->month_name d,month_name (*p),month_name
“->”的优先级等同与,.”。
struct nstud s,*p=&s;
s.birthday.year (*p).birthday.year
p->birthday.year (p->birthday).year
四个表达式等价
struct A {int len;
char *str;} s={1,”0123456”},*p=&s;
表达式 表达式的值 表达式执行的操作
++p->len 2 len增 1后访问 len
p->len++ 1 访问 len后 len增 1
*p->str 48 str所指对象‘ 0?的值
*p->str++ 48 访问 str所指对象‘ 0?
的值后 str增 1指向‘ 1?
(*p->str)++ 48 访问 str所指对象‘ 0?
后 str所指对象‘ 0?增 1
*++p->str 49 单目运算右结合,
先 str增 1,再 str所指对象‘ 1?的值
len 1
str
p
0?
1?
2?
3?
4?
5?
6?
\0?
s
8.3 结构和函数一、结构和结构指针作为函数参数将结构传递给函数有三种可用的方法:
( 1)将每一个结构成员作为一个参数;
一般仅当需要结构变量的个别成员值时使用。
( 2)将整个结构变量作为一个参数;
函数对结构变量的副本进行操作。
( 3)用指向结构变量的指针作参数。
对较大的结构变量传递效率较高,仅传递一个指针的值。
同时被调用函数可对调用函数中结构变量的值进行修改。
二、结构和结构指针作为函数返回值
( 1)返回结构的函数说明形式:
存储类型区分符 struct 类型区分符 函数名(参数表)
( 2)返回结构指针的函数说明形式:
存储类型区分符 struct 类型区分符 *函数名(参数表)
三、程序举例首先定义一表示复数的结构类型:
/*comp.h*/
struct complex {
float re; /*实部 */
float im; /*虚部 */
}
例 1:编写一函数 makecomp,生成一复数数据
#include,comp.h”
struct complex makecomp(float x,float y)
{ struct complex z;
z.re=x; z.im=y;
return z;
}
main()
{ struct complex comp1,comp2; float x1,x2,y1,y2;
scanf(“%f%f%f%f”,&x1,&y1,&x2,&y2);
comp1=makecomp(x1,y1);
comp2=makecomp(x2,y2);
}
例 2:编写一函数 addcomp,计算两复数之和。
#include,comp.h”
struct complex *addcomp(struct complex x,struct complex y)
{ static struct complex z;
z.re=x.re+y.re; z.im=x.im+y.im;
return &z;
}
main()
{ struct complex comp1={1.0,2.0},comp2={3.0,4.0},*p;
p=addcomp(comp1,comp2);
printf(“result,re=%.2f \t im=%.2f\n”,p->re,p->im);
}
例 2:编写一函数 addcomp,计算两复数之和。
#include,comp.h”
struct complex *addcomp(struct complex *x,struct complex *y)
{ static struct complex z;
z.re=x->re+y->re; z.im=x->im+y->im;
return &z;
}
main()
{ struct complex comp1={1.0,2.0},comp2={3.0,4.0},*p;
p=addcomp(&comp1,&comp2);
printf(“result,re=%.2f \t im=%.2f\n”,p->re,p->im);
}
8.4 结构数组结构数组是以同类型的结构变量为元素的数组。可用于学生登记表、职工登记表、图书目录商品销售清单、库存清单等二维表格数据。
一、结构数组的说明、引用和初始化例:说明一个具有 30个元素的学生结构数组。
struct stud{ /*定义一个能表示学生属性的结构类型 */
long num;
char name[10];
char sex;
short age;
float score;
} students[30];
结构数组逻辑上表示一个二维表格:
学生情况登记表
num name sex age score
student[0] 2129500 张三 m 18 83.5
student[1] 2129501 李四 m 19 90.5
student[2] 2129502 王红 f 18 85.
… … … … … …
student[29] 2129529 程萍 f 19 87.
表中每一行表示一个元素,即一个学生的记录,用结构变量形式表示;
结构数组的元素的引用与普通数组元素的形式完全一样;例如,student[i] 此处 student[i]为一结构变量,与一般结构变量用法相同。
例:说明一个具有 5个元素的学生结构数组。
struct stud students[5]={
{2129500,“张三”,‘ m?,18,83.5},
{2129501,“李四”,‘ m?,18,90.5},
{2129502,“王红”,‘ m?,18,85},
{2129503,“田亮”,‘ m?,19,87},
{2129504,“张灵”,‘ f?,18,81.5},
};
for(i=0;i<5;i++)
printf(“\n%10ld%15s%4c%6hd%8.1f”,student[i].num,
student[i].name,
student[i].sex,student[i].age,student[i].score);
for(sum1=0,sum2=0,i=0;i<5;i++)
{ sum1+= student[i].age; sum2+= student[i].score;}
printf(“\n%-29s%6.1%8.1f”,sum1/5.,sum2/5);
二、用结构的指针指向结构数组的成员。
例 1,struct stud students[30] 。 *p;
for(p=students;p<&students[30];p++)
printf(“\n%10ld%15s%4c%6hd%8.1f”,p->num,p->name,
p->sex,->age,p->score);
例 2,struct { int len;
char *str;
} a[2]={{1,”0123456”},{10,”98765”}},*p=a;
(++p)->len 值为 10,p先增 1,然后访问 p当前所指 a[1]的
len
(p++)->len 值为 1,访问 p当前所指 a[0]的 len,然后 p增 1.
*p++->str 值为 48,访问 p当前所指 a[0]的 str[0],然后 p增 1.
*(++p)->str 值为 57,p先增 1,然后访问 p当前所指 a[1]的 str[0]
注意下述表达式的区别:
(++p)->len 与 ++p->len,后者是 len增 1
(p++)->len 与 p++->len相同
,两者均为 p增 1
*p++->str 与 *(p++->str)相同,*作用于 str上,++作用于
p上
*(++p)->str 与 *++p->str不同
,后者是 str加 1
p?0?
1?
2?
3?
4?
5?
6?
\0?
a[0] len 1
str
a[1] len 10
str
9?
8?
7?
6?
5?
\0?
三、结构的大小与 sizeof运算符结构的大小 是指一个结构变量占用的存储空间的字节数,
一般等于一个结构变量所有成员的存储字节数的总和。
对于结构中长度不满一个字长的成员,例如 char,short类型的成员在 32位机中,不同编译在实际分配存储单元时的处理方法可能不同:
( 1)一种是使成员的地址在 字边界 对齐,于是结构变量中留下一个或多个不用的空白空间,这种情况下,结构的大小大于所有成员的存储字节数的总和 。
( 2)另一种是使成员的地址在 字节边界 对齐,这种情况下,
结构的大小 等于所有成员的存储字节数的总和 。
基于上述原因,为提高程序的可移植性,当需要使用结构的大小时,不能有人工计算,应使用 sizeof运算符来计算。
一般用法:
sizeof (对象名) 如变量名,常量名等
sizeof (类型名)
例:
float x; const int YES=1;
struct stud student,students[30];
sizeof( char) 1,sizeof(short) 2,sizeof(float) 4
sizeof (x) 4,sizeof(YES) 2
sizeof (struct stud) 15
sizeof (student) 15
sizeof (students) (21或 22) *30
四、结构数组作函数参数结构数组作函数参数时,形式参数为数组元素结构类型的指针,实参可以是结构数组名或结构数组第 0个元素的地址。
例:根据学生的成绩按从高到低排序。
#include,stdio.h”
define NSTUDENTS (sizeof( students)/sizeof (struct stud)) /*学生人数 */
struct stud{ long num;
char name[10];
char sex;
short age;
float score;
} students[]={
{2129500,“张三”,‘ m?,18,-1},{2129501,“李四”,‘ m?,18,
-1},
{2129502,“王红”,‘ m?,18,-1},{2129503,“田亮”,‘ m?,19,
void readscore(struct stud *,int);
void sortbyscore(struct stud *,int);
void writescore(struct stud *,int);
void main()
{
printf(“input %d scores:\n”,NSTUDENTS);
readscore(students,NSTUDENTS);
sortbyscore(students,NSTUDENTS);
printf(“num\tname\tsex\tage\score\n”);
writescore(students,NSTUDENTS);
return 0;
}
void readscore(struct stud *stab,int numbers)
/*输入 numbers学生的成绩 */
{
int i;
float temp;
for(i=0;i<numbers;i++)
{
scanf(“%f”,&temp);
stab[i].score=temp;
}
}
void sortbyscore(struct stud *stab,int numbers);
/*根据成绩递减排序,选择排序法 */
{ int i,j;
struct temp;
for (i=0;i<numbers-1;i++)
for(j=i+1;j<numbers;j++)
if (stab[i].score< stab[j].score)
{ temp=stab[i];
stab[i]=stab[j];
stab[j]=temp;
}
}
void writescore(struct stud *stab,int numbers)/*输出成绩清单 */
{ int i;
for (i=0;i<numbers;i++)
printf(“%-ld\t%-s\t%-c\t%-d\t%-0.2f\n”,
stab[i].num,stab[i].name,stab[i].sex,
stab[i].age,stab[i].score);}
输出,input 4 score:
输入,84 69 91 88
输出,num name sex age score
2129502 王红 m 18 91.00
2129503 田亮 m 19 88.00
2129500 张三 m 18 84.00
2129501 李四 m 18 69.00
8.5 结构和指针的应用
8.5.1 动态数据结构
数据结构是一种类型的数据在机内的存储结构,目前为止所接触到的都是静态数据结构,静态数据结构通过变量说明建立,变量所占用的存储器空间的大小是在变量说明时确定的,
程序执行过程中不能改变。
动态数据结构是在程序运行过程中通过调用系统提供的动态分配函数根据程序要求分配连续存储空间,没有相应的变量名,一般用指针指向这连续存储空间。
动态数据结构适应数据成员个数不固定,或需要经常增加或减少的数据,
例如,struct stud students[50];
结构数组表示一个班的学生记录,不能很好的满足各种情况。
动态数据结构可解决此类问题,根据实际情况分配记录空间。
data next
stud1
head data next
stud2
data next
studn null……
这种通过一个指针将一个数据元素和另一个数据元素相连的数据存储形式称为 单向链表 。
head
stud1
nextdata
stud2
nextdata
studn
nextdata
……
单向循环链表 。
null
front data behide
stud1
head
……
front data behide
stud2
front data behide
nullstudn
front data behidehead front data behide
stud2
front data behide
studn……
双向链表双向循环链表
stud1
动态数据结构的元素称为,结点,。每个结点是类型相同的一个结构变量。与数组不同,每个结点的存储空间不一定相邻。
结点由两部分组成,
( 1) 数据域,存放被处理的任何类型数据;
( 2) 指针域,存放下一结点的地址(指针),指针的类型为结点的类型,即为指向自身的结构指针。
例如:单链表结点定义:
struct stud_node
{ struct stud data; /*数据域,存放一个学生记录 */
struct stud_node *next; /*指针域,指向下一学生记录
*/
}
8.5.2 C的动态存储分配函数
( 1) void *malloc(size_t size)
功能:向系统申请 size字节的存储空间。分配成功时,返回分配的连续存储空间的首地址;分配失败则返回 NULL。
新分配的存储区未被初始化。
例,#define SIZE 10
int i,*p;
p=malloc(SIZE*sizeof(int)); /*分配 10个整型数的存储空间 */
if (p==NULL) {printf(“分配失败 !”); exit(0);}
for(i=0;i<SIZE;i++)
scanf(“%d”,&p[i]);
( 2) void *calloc(size_t nobj,size_t size)
功能:向系统申请有 nobj个元素的数组存储空间,每个元素的大小为 size字节的存储空间,共向系统申请 nobj× size 个字节的存储空间。分配成功时,返回分配的连续存储空间的首地址;分配失败则返回 NULL。
新分配的存储区均被初始化为 0。
例,int i,num; struct stud *p
printf(“输入班级人数:” );
scanf(“%d”,&num); /*分配存储 num个学生记录的存储空间 */
p=calloc(NUMBERS,sizeof(struct stud));
if (p==NULL) {printf(“分配失败 !”); exit(0);}
for(i=0;i<num;i++)
{ /*依次输入学生记录信息 */ }
( 3) void *realloc(void *p size_t size)
功能:向系统重新申请有大小为 size字节的存储空间,p指向以由 malloc和 calloc分配的存储空间。分配成功时,返回分配的连续存储空间的首地址;分配失败则返回 NULL。
例,struct stud *p1,*p2;
p1=malloc(30*sizeof(struct stud)); /*分配 30个学生记录空间 */
if (p1==NULL) {printf(“分配失败 !”); exit(0);}
for(i=0;i<num;i++)
{ /*依次输入学生记录信息 */ }
p2=malloc(p1,50*sizeof(struct stud)); /*分配 50个学生记录空间 */
if (p2==NULL) {printf(“重新分配失败 !”); exit(0);}
田亮 …第 30个学生记录第 29个学生记录
…
…
…
…
…
王红 …第 3个学生记录李四 …第 2个学生记录张三 …第 1个学生记录
p1
第 50个学生记录
…
…
…
…
第 30个学生记录第 29个学生记录
…
…
第 3个学生记录第 2个学生记录第 1个学生记录
p2
张三 …
李四 …
王红 …
…
…
…
田亮 …
( 4) void free(void *p)
功能,释放向系统重新申请存储空间,p指向以由 malloc、
calloc和 realloc分配的存储空间。
注意,动态分配的存储空间一定要用 free函数释放,而静态分配的存储空间当程序运行完成的时候自动释放。
例,struct stud *p1,*p2;
p1=malloc(30*sizeof(struct stud)); /*分配 30个学生记录空间 */
for(i=0;i<num;i++)
{ /*依次输入学生记录信息 */ }
p2=malloc(p1,50*sizeof(struct stud)); /*分配 50个学生记录空间 */
free(p1); ……
free(p2);
stud1
nextdatahead
stud2
nextdata
nullstudn
nextdata
……
8.5.3 链表头指针 链头 链尾说明:
用头指针表示链表;
head->data; 第一个学生的记录数据
head->data.num 第一个学生的学号
head->data.name 第一个学生的姓名
head->next; 第一个结点的指针域,第二个结点的指针
head->next->data 第二个结点的记录数据
head->next->next 第二个结点的指针域,,第三个结点的指针空链表条件,head==NULL
访问链表所有结点:
struct stud_node *p;
p=head; /*p指向第一个结点 */
while (p!=NULL) /* p指向的结点存在 */
{
printf(“%s\n”,p->data.name); … /* 访问 p指向结点的数据 */
p=p->next; /*p指向下一个结点 */
}
一、链表的建立和输出根据插入新结点的方式不同,链表分为:
( 1),先进先出”链表:
a1
nextdatahead
a2
nextdata
nullan
nextdata
……
( 2)“先进后出”链表:
an
nextdata
an-1
nextdatahead
nulla1
nextdata
……
例:输入一批整数,以 0为结束,将它们建成一个先进后出链表
( 0不包含在链中),然后输出所有结点的值。
( 1) 结点类型定义:
struct node {
int data;
struct node *next;
}
( 2)每输入一个整数 n,
( A) 建立一个结点,有指针变量 p指向:
p=malloc(sizeof(struct node));
p->data=n;
( B) 将指针变量 p指向的结点插入链:
空链表时的插入,
head null
1
nextdata
p null
2
nextdata
head 1
nextdata
3
nextdata
p
非空链表时的插入,
p->next=head;
head=p;
p->next=head;
head=p;
#include,stdio.h”
#include,stdlib.h”
struct node { int data;
struct node *next;
}
main()
{ int n;
struct node *p,*head;
head=NULL; /*链表初始化 */
printf(“输入整数序列,以 0结束,”);
scanf(“%d”,&n);
while (n) { p=malloc(sizeof(struct node));
p->data=n;
p->next=head;
head=p;
scanf(“%d”,&n); }
p=head;
while(p) { printf(%6d”,p->data);
p=p->next; }
}
输入,1 2 3 4 5
输出,5 4 3 2 1
例:输入一批整数,以 0为结束,将它们建成一个先进先出链表
( 0不包含在链中),然后输出所有结点的值。
( 1) 结点类型定义:
struct node {
int data;
struct node *next;
}
( 2)每输入一个整数 n,
( A) 建立一个结点,有指针变量 p指向:
p=malloc(sizeof(struct node));
p->data=n;
( B) 将指针变量 p指向的结点插入链:
head
p
/// 1 2 3 4 5
p p p p
头指针 头结点
tail
head=malloc(sizeof(struct node))
tail=head
tail
p=malloc(sizeof(struct node))
tail->next=p
tail=p
p=malloc(sizeof(struct node))
tail
tail->next=p
tail=p
tail tail tail
tail->next=NULL;
0
#include,stdio.h”
#include,stdlib.h”
struct node { int data;
struct node *next;
}
main()
{ int n;
struct node *p,*head,*tail;
head=malloc(sizeof(struct node)); /*链表初始化 */
tail=head;
printf(“输入整数序列,以 0结束,”);
scanf(“%d”,&n);
while (n) { p=malloc(sizeof(struct node));
p->data=n;
tail->next=p;
tail=p;
scanf(“%d”,&n); }
tail->next=NULL; p=head->next; /*p指向首结点 */
while(p) { printf(%6d”,p->data);
p=p->next; }
}
输入,1 2 3 4 5
输出,1 2 3 4 5
///
head
12 2 31 4 10
p
二、删除 p指向的链表结点
…
q
q->next=p->next;
free(p);
int delete_node(struct node *head,int x)
{
struct node *p,*q;
q=head; p=head->next; /*q指向头结点,p指向第一个结点 */
while (p && p->data!=x)
{ q=p; p=p->next;}
if (p==NULL) return 0; /*符合条件的结点不存在 */
q->next=p->next;
free(p);
return 1; /*符合条件结点已删除 */
}
///
head
2 12 31 34 078
三、插入一个结点到按结点数据域递增排序的链表中,使其仍然递增。
…
f 28
pq pq pq
f->next=p;
q->next=f;
int insert_node(struct node *head,int x)
{ struct node *p,*q,*f;
f=malloc(sizeof(struct node));
if (f==NULL) {printf(“新结点不能建立” ); return 0;}
q=head; p=head->next; /*q指向头结点,p指向第一个结点 */
while (p && x > p->data) /*查找定位 */
{ q=p; p=p->next;}
f->next=p;
q->next=f;
return 1; /*插入成功 */
}
int main()
{ struct node *head; int n;
head=malloc(sizeof(struct node));
if (head==NULL) {printf(“链表不能建立” ); return 0;}
head->next=NULL; /*链表初始化 */
printf(“输入整数序列,以 0结束,”);
scanf(“%d”,&n);
while (n) { insert_node(head,n);
scanf(“%d”,&n); }
………… /* 其它处理 */
}
四、链表排序首先,统计出结的个数 n,再按照选择排序法进行排序,
第一趟处理完后,第一结点值最大,第一趟处理完后,第二结点值最大,依此类推。 下例中链表为结点数据为整型的 带头结点 的形式。
void sortchain(struct int node)
{ int i,temp,n=0;
struct intnode *p1,*p2;
p1=head->next; /*p1指向第一个结点 */
while (p1!=NULL) /*统计结点个数 */
{ n++;
p1=p1->next; /*p1指向下一个结点 */
}
/*i控制 n-1趟选择 */
/*p1依次指向第 1、第 2、第 3、。。。、第 n-1个结点 */
/*p2依次指向 p1后的所有个结点 */
for(p1=head->next,i=1; i<n; i++,p1=p1->next)
for(p2=p1->next; p2!=NULL; p2=p2->next)
if (p1->data<p2->data)
{ /*交换结点的值 */
temp=p2->data;
p2->data=p1->data;
p1->data=temp;
}
}
8.6 联 合前面所述的所有数据中,类型是固定的;
实际应用中,有些数据在不同场合表现为不同类型的数据;如政治面貌,在职工(学生)管理系统中是一个人的属性,或者是群众,用字符串表示(也可以干脆空白) ;或者是党(团)员,则进一步要求填写入党(团)时间,入党
(团)地点和入党(团)介绍人,此时必须用结构类型数据表示 。
联合类型可满足这一类要求。 联合类型的变量(简称联合) 由多个成员组成,这些 成员并不同时存在,而是在不同时刻拥有不同的成员,在同一时刻仅拥有其中一个成员 。
联合的成员可以是任何类型的数据,包括任何基本类型、指针、数组、结构和联合。
一,联合的说明、引用和初始化除用关键字 union代替 struct外,联合类型的定义,联合变量的说明及联合成员的应用在语法上与结构完全相同;
例如:假定一种数据对象可能是 int类型,double类型或字符类型,为了在同一个存储区域存放这样的数据对象,可说明如下联合:
union {
char ch; /*1字节 */
int i; /*2字节 */
float f ; /*4字节 */
} u,* p=&u;
u.ch=?a?; printf(“%C:%d”,u.ch,p->ch); /*输出 c,97*/
u.i=450; printf(“%d,%d”,u.i,p->i); /*输出 450,450*/
u.f=450; printf(“%02f,%.2lf”,u.f,p->f);
/*输出 450.00,450.00*/
u.ch=20;
u.i=266; printf(“%d,%d”,u.ch,u.i);
/*输出,10,266 其中,10是错误数据,因此时成员 i有效 */
u.f=450; printf(“%c,%d,%.2f”,u.ch,(*p).i,p->f);
/*输出:,0,450.00 其中,仅成员 d有效,450为正确数据 */
注意,(1)使用联合变量时,必须知道当前有效成员。
(2) 联合的大小大于或等于最长成员的字节数例:联合变量作为结构成员:
职工档案管理中,婚姻状态:
1。未婚:仅需要一标志;
2。已婚:配偶姓名,孩子数;
3。离婚:日期。
struct date { char month[4];
int day;
int year;
}
enum marristate {married,divorce,single};
struct employee {
char num[15];
char name[10];
char sex;
enum marristate state; /*婚姻状态,
union {
struct{ char sname[10];
int childnum;
} spouse;
struct date divorcedate;
} statemess;
} emp[300];
/*输入职工信息 */ printf(“…………”); /* 输出表头 */
for(i=0;i<numbers_of_employee; i++}
{ printf(“%20s%12s%s”,emp[i].num,emp[i].name;
emp[i].sex==?m? && emp[i].sex==?M,男”:“女” );
switch (emp[i].state) {
case single,printf(“未婚” ); break;
case married,printf(“已婚 %12s%4d”,
emp[i].statemess.spouse.sname,
emp[i].statemess.spouse.childnum);break;
case divorce,printf(“离婚 %s%4d%6d”,
emp[i].statemess.divorcedate.month,
emp[i].statemess.divorcedate.day,
emp[i].statemess.divorcedate.year);
} /*end of switch */
} /*end of for*/
8.7 typedef说明
typedef说明为类型定义一个名字,相当于原类型的别名。
一,typedef说明的形式
typedef 类型区分符号 说明符例 1,typedef int INTEGER; /*定义 int类型别名 */
INTEGER i,j;
例 2,typedef char *STRING; /*定义字符指针类型别名 */
STRING str1=“abc”,str2=“123”;
例 3,typedef int VECTOR[50];
/*定义 VECTOR是 int[50]别名 */
VECTOR v1,v2; /*v1,v2都是长度为 50整型的数组 */
例 4,typedef struct stud{
long num;
char name[10];
char sex;
short age;
float score;
} STUD; /*/定义结构类型 struct stud的别名 STUD*/
STUD student,*p;
例 5,typedef int FI(char *,char *)
FI是类型为,int (char *,char *)的别名,表示有两个字符指针参数,返回类型为 int的函数。
FI strcmp,strcat; /*两函数的说明 */
二,typedef说明的用途
( 1)使程序参数化,便于程序移植。
例如数据类型 char的符号问题,int的长度问题,与编译环境有关,导致程序需要进行大量修改才能正常运行,如果给其定义一个别名,程序中说明变量时使用别名,这样一旦发生环境改变,仅需修改别名定义。
例:在 32位机上:
typedef int INTEGER;
INTEGER i,j; /*i,j占 4字节 */
在 16位机上仅需修改别名定义:
typedef long INTEGER;
( 2)提高可读性,编辑效率。
例 1,typedef unsigned size_t
void *malloc(size_t size);
等给现有类型定义一个有一定意义的别名。
例 2,typedef struct stud STUD;
STUD stu1,stu2;
注意,typedef 没有定义新类型,仅仅为现有类型定义一个类型的别名。
定义别名方法:(定义一个 4*2的整型数组类型)
(a) int INT_MATRICS[4][2];
(b) typedef int INT_MATRICS[4][2];