Upload
leonard-alvarado
View
59
Download
0
Embed Size (px)
DESCRIPTION
第11章 结构体与共同体. 11.1 概述 11.2 结构体变量的定义及使用 11.3 结构体数组 11.4 结构体指针 11.5 链表 11.6 共用体. 11.1 概述. 我们已讨论过的数据类型有简单型(整、实、字)、数组类型和指针类型。这些变量之间无固定内在的联系,分别表示独立数据,无从属关系。 数组虽能同时处理多个数据,但必须是同类型的。指针类型依赖于所指变量的类型。 这些数据类型难以处理 有从属关系 的 数据结构 。. 例如: 在计算机等级考试管理系统中,每个考生都 有如下信息 :. - PowerPoint PPT Presentation
Citation preview
第第 1111 章 结构体与共同体章 结构体与共同体11.1 概述11.2 结构体变量的定义及使用11.3 结构体数组11.4 结构体指针11.5 链表11.6 共用体
11.1 11.1 概述概述
我们已讨论过的数据类型有简单型(整、实、
字)、数组类型和指针类型。这些变量之间无固定
内在的联系,分别表示独立数据,无从属关系。
数组虽能同时处理多个数据,但必须是同类型的。
指针类型依赖于所指变量的类型。
这些数据类型难以处理有从属关系的数据结构。
例如:在计算机等级考试管理系统中,每个考生都
有如下信息 :
考号 : 长整型
姓名 :字符串
性别 :字符
年龄 :无符号整型
报考等级:字符串
成绩 :实型
数据分属不同的
类型,但有内在的联
系。
C 语言提供了解
决这类问题的类型—
—结构体类型。
11.2.1 类型的作用11.2.1 类型的作用
11.2 11.2 结构体变量的定义及使用结构体变量的定义及使用
确定该类型数据的表示形式,取值范围、存储结构及其允许参加的运算。
∴ 在程序中每个变量在使用前要确定其类型,要先定义后使用。
例如: int x; float y; char a[30];
这些常用类型由系统定义好,直接拿来
用即可。而实际应用中的类型是千变万化
的,因此,数据的类型应根据实际情况由
用户来定义。
结构体类型则属于用户自定义类型。
11.2.2 结构体变量的定义11.2.2 结构体变量的定义
struct student
{ int num;
char name[20];
char class[10];
char sex;
float score;
} ;
方法一: 先定义类型,再用类型说明变量。
struct student stu1, stu1;
方法二:在定义类型时,直接说明变量。
struct student
{ int num;
char name[20];
char class[10];
char sex;
float score;
}stu1,stu2;
类型名
成员名
注: 成员名与变量名可相同,但意义不同。
方法三: 嵌套定义。(如果有需要的时候) struct student1
{ int num;
char name[20];
char class[10];
char sex;
struct date birth;
float score[4];
}stu;
struct date
{
int month;
int day;
int year;
};
stu
num name sex agebirthday
month day year score
• 先定义类型,再用类型说明变量
• 在定义类型时,直接说明变量
• 用一个已定义的结构类型来定义另一结构类型中的成员—— 嵌套定义
都符合标识符先定义后使用的原则。
结构体变量中的各成员在内存中占一片连续的单元,其长度为: 各成员所占单元之和
11.2.3 结构体变量的访问11.2.3 结构体变量的访问
由于结构类型是一个复合类型,其由多个成
员组成,因此不能访问结构体变量,只能按如下
方法访问变量的成员。
变量名 . 成员名
变量名 . 一级成员 . 二级成员 (当嵌套时)
一、结构体变量的初始化: struct student stu={ 2001802, “ 李” , “ 计 011”, ‘f ’, 85.2 };
struct student 1 stu={ 2001802, “ 李” , “ 计 011”, ‘f ’,
12, 3, 1986, 85.2 };
结构体变量中的各成员在内存中占一片连续的单元,其长度为: 各成员所占单元之和
struct s
{ long num;
char name[20];
char sex;
}student={ 97031, "li ming" , 'm' } ;
struct stu
{ long num=97031;
char name[20] ="li ming";
char sex='m';
}student ;
错!!!不能在类型定义中,对成员项赋值!
二、结构变量的引用:
1 、不能将结构体变量作为一个整体输入、
输出(这一点和数组相同)。例如:
不能 scanf("%d", &stu );
只能 scanf("%d", student.num );
scanf("%s", student.name );
student.sex=getchar( );
student.birth.day=12;
嵌套的结构体变量只能访问最低级的成员。
2 、两个同类型的结构体变量可互相整体赋值。 例如: struct s
{ long int no;
float score;
}stu1,stu2={ 200123, 76.5 };
stu1=stu2;
3 、对成员可根据其类型进行相应的运算。
4 、可引用成员的地址,也可引用结构体变量
的地址。
单个结构体变量没有实际的意义,可用简单变量来完成工作。仅当结构体与数组相结合时,才能解决复杂问题。
结构体数组 : 用结构类型定义数组。即每个数
组元素是结构类型。
11.3 11.3 结构体数组结构体数组
二、结构体数组的引用:
1 、可引用数组元素的域,不能引用数组元素。因为每个数组元素是结构体类型。
例如: stu[1]=97001; 错误
stu[1].num=97001; 正确
一、结构体数组的定义及初始化: 见 p.266
例程 1 :
某单位有 30 人参加计算机等级考试。现需将每
名考生的准考证号、姓名、性别、报考级别和成绩
录入计算机,并统计平均分和及格率。
2 、同一结构体类型变量及数组元素之间可整体 赋值。 struct examinees s1,s[10];
s1=s[2]; s[1]=s[0]; 正确
11.4 11.4 结构体指针结构体指针
一、结构变量指针:用结构类型定义指针。即存放 结构对象的首地址。
例如: struct student
{ int num;
char name[20];
char sex;
float score;
};
struct student x,*p;
p=&x;
由于结构类型变量占用连续单元,故 p 存放
num 成员的首地址。
访问成员时,有三种表示:
结构体变量 . 成员
p 成员名
( *p ) . 成员名
注: *p . 成员名 错, *比 . 级别低
二、指向结构体数组的指针
P.269 例 11.4
11.5 11.5 链 表链 表
前面介绍的内容都是静态的数据结构。即事先
定义类型,由编译分配好单元,以便执行程序时供
对象存放数据。静态数据结构的弊端是显然的,尤
其是用于数组。本章介绍的是动态数据结构。
11.5.1 链表的概述11.5.1 链表的概述
由于是动态地分配存储单元,使得被分配的
单元是独立的,即单元之间的地址不连续,从而
不能利用已学过的方法访问这些单元。可利用链
表来解决上述矛盾。
动态数据结构:在程序运行时,根据需要分
配存储单元。多用于数组,以解决须事先定义长
度的弊端。
一、什么是链表?
一种动态分配的数据结构。即用指针变量和结构变量将非连续的数据块连成一个整体的一种数据结构。
例如:A B C
上述三个变量间没有任何联系,若希望象
数组那样,借助于一个变量的首地址,就能访
问到其他变量。
只能按如下方法处理:
将 B 的地址存放到 A 中,将 C 的地址存放到 B 中,这样就可以通过第一个变量找到其他变量。
每个结点必须是结构变量,至少有两个成员:
一个用来存放该结点的数据,另一个存放下一
个结点的地址。
链表:将若干数据项按一定的原则连接起
来的表,表中由若干元素组成,每个元素称
为结点。
&stu2
78
0001
&stu3
67
0002
NULL
80
0003&stu1
head stu1 stu2 stu3
例如:
借助于链表,可将那些物理上无序的单元,在逻辑上变得有序,从而达到利用动态数据结构访问多个单元的目的。
1 、 可动态地分配和释放存储空间。
2 、 只能通过前一个结点才能找到下一个结点,
即只能顺序访问结点 ,不能象数组 那样随
机访问。
二、链表的特点:
3 、链表中需设置一个指向第一个结点的头指
针,且最后一个结点设置为 NULL 。
头指针很重要,它代表链表的首地址,故不要轻易地改变。
三、链表的分类:
单链表、环链表、双链表 …
&stu2
78
0001
&stu3
67
0002
NULL
80
0003&stu1
head stu1 stu2 stu3
单链表、环链表、双链表 …
&stu2
78
0001
&stu3
67
0002
&stu1
80
0003&stu1
head stu1 stu2 stu3
11.5.2 动态内存分配与释放函数11.5.2 动态内存分配与释放函数
1 、动态内存分配函数:
void * malloc(size); void *calloc( n, size );
在程序运行需要时把一片存储区的起始地址赋给指针变
量,不需要时可利用释放函数变为自由单元。
2 、动态内存释放函数: void free (void *p);
三函数的头文件为:"malloc.h"
内存分配函数,在动态区中分配一个长度为 size 的连续空间,分配成功,返回一个指向该空间首地址的指针,未成功,返回值= 0 。
内存分配函数,在动态区中分配一个长度为 size 的连续空间,分配成功,返回一个指向该空间首地址的指针,未成功,返回值= 0 。
void * malloc(size);
动态内存分配函数:
11.5.3 链表的使用11.5.3 链表的使用
一、链表的建立和输出 :
步骤:
1 、 定义 3 个结构体指针
head :保存链表首结点地址 ,
prear :保存链表当前尾结点地址,
pnew :新结点地址。
2 、给 3 个指针赋初值,建立第一个结点:
head = NULL ;
pnew = (struct stu *)malloc(LEN) ;
head = pnew ;
prear = pnew ;
并给首结点各域赋值。
3 、在循环中 :
1) 、利用 malloc 函数,产生动态单元。
2 )、给动态单元赋值。
3 )、将动态单元挂到链表的尾部。
#define LEN sizeof( struct student )
#define STU struct student
struct student
{ long num;
float score;
struct student next;
};
同时定义三个指向这种类型的指针变量 head, pnew, prear.
例程: 写一个函数,建立 5 名学生数据的单向链表,每一个
学生的数据块 ( 结点 ) 定义如下:
建立链表
STU creat( )
{ // 赋初值
STU head, p1, p2 , int n=0;
head=NULL;
p1= (STU ) malloc(LEN);
scanf("%d, %f ", &p1num, &p1score);
p1 = (STU ) malloc(LEN);
scanf("%ld, %f", &p1num, &p1score);
}
p2next = NULL; 给尾指针赋值
return(head);
}
while ( p1num != 0 )
{ n+ +;
if( n= =1) head=p1 ; 将结点挂到表尾 else p2next = p1 ;
p2=p1; 使 p2 指向当前表尾
void print ( STU head )
{ STU p ;
p = head ;
if (p = = NULL) return ;
do
{ printf("%ld %5.1f \n", pnum, pscore) ;
p = pnext ;
}while (p != NULL) ;
}
输出链表
&stu2
78
0001
&stu3
67
0002
NULL
80
0003&stu1
head stu1 stu2 stu3例如:
二、链表的删除和插入
链表的删除 :
删除结点操作:
若用 2 个指针操作 p2.next = p1.next
若用 1 个指针操作 p.next = p.next.next
p2 p1
一般给出某一条件,当某一条件成立时,则删除该结点:
STU del( STU head, long num )
{ STU p1, p2 ;
if ( head= =NULL )
{ printf("\n list null!=\n"); return (head); }
1 、表为空时,无任何删除
2 、当第一个结点被删除时,修改表头
3 、当最后一个结点不满足条件时,返回。
p1 = head;
while (num!=p1num && p1next!=NULL)
{ p2=p1; p1=p1next; } 寻找删除点
if (num= =p1num)
{ if (p1= =head) head=p1next;
else p2next=p1next;
printf ("delete: %ld \n", num); n – – ;
}
else printf ("%ld not been found!\n", num);}
一般链表中结点的关键数据按顺序输入,待插入结点的关键数据与链表中的关键数据一一比较,插入适当的位置。
链表的插入 :
001 003
\0
004
002
head例如:
插入操作:new.next=p.next;
p.next=new;
步骤:
1 、定义 3 个指针 new( 指向待插入结点 ) , p1( 当前待操作结点 ) , p2 ( p1 之前的结点),并赋初值。
2 、若链表为空,则插入到表头,作为第一个结点。
3 、若链表不为空,寻找插入点。 1 )、若插入到第一个结点之前,则改变头指针的值。 2 )、若插入在表中或表尾,则执行相应的插入操作。
算法:
STU insert( STU *head, STU *new ) 待插入的结点可在函
{ STU *p1,*p2; 数中输入
p1=head;
if( head==NULL) 若为空表,将结点挂
{ head=new; newnext=NULL; } 在头结点
else
while(( newnum > p1num )&&( p1next != NULL ))
{ p2=p1; p1=p1next; } 寻找插入点
if( newnum <=p1num )
{ if (head == p1) head=new; 插入在表头
else p2next=new; 插入在表中
newnext=p1;
}
else
{ p1next=new ; newnext=NULL; } 插入在表尾
n++;
return( head );
} 11-7-1.c 11-7-11.c
11.6 11.6 共有体共有体
二、共用体数据类型的特点
几个不同类型的数据共用同一个存储单元。共用体变量
所占内存长度 =最长成员的长度,各成员轮流使用一个单
元,所以共同体变量及其各成员地址相同。每一瞬间只能
存放一个成员,共用体中起作用的是最后一次存入的成员。
一、共用体类型定义
把结构体类型中的关键字 struct换成 union 即可。
例如: struct memb{ float v;
int n;char c;
} stag;
stag 占内存 7个字节的空间
union memb{ float v;
int n;char c;
} ustag;
utag 占的内存空间为
1001
1005
1007
v
nc
2001
v
nc
2002
2003
2004
共用体类型变量每次只能存放一个成员的值。
三、共用体类型变量的引用
( 共用体类型变量名 ). 成员名
1 、共用体类型变量的访问同结构体类型变量。
2 、不能对共同体变量初始化。
union
{ int i;
char ch;
}x={6, ‘A’} ; 错误
例程 11-6-1: #include<stdio.h>
union memb
{ float v;int n;char c;
} ;
main( )
{ union memb utag;
utag.v=36.7
utag.c='T '
utag.n=18
printf("%5.1f\n%d\n%c\n", utag.v, utag.n, utag.c); }
运行结果: 36.7
13107
想一想: 若改变成员的赋值顺序:
utag.v=36.7
utag.c='T '
utag.n=18
则运行结果为: 36.5
84
T
例程 11-6-2: ( 11-6-2.c )
某单位有 3 名职工参加计算机办公应用技能考试。设每个人的数据包括考号、姓名、年龄、和成绩。单位规定年龄为 25岁以下的职工进行笔试,成绩为百分制, 60 分以上为及格。而 25岁以上的职工进行操作考试,考试成绩为 A 、 B 、 C 、 D , C 级以上为及格。请统计考试及格的人数,并输出每位考生考试的成绩。