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