C程序设计结构体联合体及位运算_第1页
C程序设计结构体联合体及位运算_第2页
C程序设计结构体联合体及位运算_第3页
C程序设计结构体联合体及位运算_第4页
C程序设计结构体联合体及位运算_第5页
已阅读5页,还剩25页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第11章 结构体、联合体与位运算本章介绍结构体、联合体及枚举类型等三种新的构造型数据类型以及位运算的基本方法,包括结构体的含义;结构体类型变量的定义、引用及初始化方法;结构体数组的定义和数组元素的引用;结构体类型指针的概念及链表的基本操作方法;联合体的含义;联合体类型变量的定义方法;枚举类型的定义; TYPEDEF的作用和位运算的基本方法等。11.1 结构体类型通过前面有关章节的学习,我们认识了整型、实型、字符型等C语言的基本数据类型,也了解了数组这样一种构造型的数据结构,它可以包含一组同一类型的元素。但仅有这些数据类型是不够的。在实际问题中,有时需要将不同类型的数据组合成一个有机的整体,以便

2、于引用。例如,在新生入学登记表中,一个学生的学号、姓名、性别、年龄、总分等,它们属于同一个处理对象,却又具有不同的数据类型。如图11-1。每增加、删减或查阅一个学生记录,都需要处理这个学生的学号、姓名、性别、年龄、总分等数据,因此,有必要把一个学生的这些数据定义成一个整体。学 号(整型)姓 名(字符型)性 别(字符型)年 龄(整型)总 分(实型)11301Zhang PingF19496.5图11-1虽然数组作为一个整体可用来处理一组相关的数据,但不足的是,一个数组只能按序组织一批相同类型的数据。对于一组不同类型的数据,显然不能用一个数组来存放,因为数组中各元素的类型和长度都必须一致。为了解决

3、这个问题,语言中给出了另一种构造数据类型“结构体”。 11.1.1 结构体类型与结构体变量结构体是一种构造类型,它由若干“成员”组成。每一个成员可以是一个基本数据类型或者又是一个构造类型。结构体既然是一种构造而成的数据类型,那么在使用之前必须先定义它,如同在调用函数之前要先定义或声明一样。定义一个结构体类型的一般形式为:struct 结构体名 成员1类型1;成员2 类型2;.成员n 类型n;“结构体”这个词是根据英文单词structure译出的。结构体中的每个成员均须作类型说明,成员名的命名应符合标识符的书写规定,成员名可以与程序中的变量名同名,二者不代表同一对象,互不干扰。例如:struct

4、 student int num;/* 学号 */char name20;/* 姓名 */char sex;/* 性别 */int age;/* 年龄 */float score;/* 成绩 */;在上述定义中,struct student是结构体类型名,其中struct是关键字,在定义和使用中都不能省略。该结构体由5个成员组成。第一个成员为num,整型变量,当然,在实际应用中我们也常常把学号定义为字符型;第二个成员为name,字符数组;第三个成员为sex,字符型;第四个成员为age,整型;第五个成员为score,实型变量。应注意末尾的分号是必不可少的。数据类型和变量是两个不同的概念。有了一种

5、结构体类型之后,就可用它去定义变量,就象用int 去定义一个整型变量那样。定义结构体类型的变量有以下三种方法。1.先定义结构体类型,再定义变量。例如:struct student int num; char name20; char sex; int age; float score;struct student student1,student2;本例中,在定义了struct student这个结构体类型之后,再用这个类型标识符去定义了两个结构体变量student1与student2。为了使用方便,也可以在程序开头定义一个符号常量来表示一个结构体类型。例如上例可改写成:#define STU

6、 struct student.STU int num; char name20; char sex; int age; float score;STU student1,student2;2.在定义结构类型的同时定义结构体变量。例如:struct student int num; char name20; char sex; int age; float score; student1,student2;这是一种紧凑形式,既定义了类型,同时又定义了变量。如果需要,下文还可再用struct student定义其它同类型变量。它的一般形式为:struct 结构体名 成员1类型1;成员2 类型2;

7、.成员n 类型n;变量名表列;3.直接定义结构体变量。例如:struct int num; char name20; char sex; int age; float score; student1,student2;直接定义了两个结构体变量student1与student2。这种方法省去了结构体名,缺点是若下文再想定义同类型的变量就不便了。上述三种方法中定义的变量student1与student2都具有下图所示的结构,其所有的成员都是基本数据类型或数组类型。numnamesexagescore图11-2若想将其中的age换成出生日期birthday,定义成含有年份、月份、日期三个子成员的类型

8、,如图11-3所示,则需先定义一个struct date日期类型,再用它去定义birthday。这就形成了嵌套的结构体。numnamesexbirthdayScoreyearmonthday图11-3按图可给出以下结构定义:struct date int year; int month; int day;struct student int num; char name20; char sex; struct date birthday; float score; student1,student2;首先定义一个结构体类型struct date,由month(月)、day(日)、year(年)

9、 三个成员组成。 再将它用到struct student类型的定义中,使其中的成员birthday被定义为struct data类型。类型与变量是不同的概念,不要混同。对结构体变量来说,在定义时一般先定义一个结构体类型,然后定义变量为该类型。只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间。11.1.2 结构体变量的引用1.引用结构体变量中的一个成员由于一个结构体变量包含多个成员,要访问其中的一个成员,必须同时给出这个成员所属的变量名以及其中要访问的成员名本身,引用方式为:结构体变量名.成员名其中的圆点符号称为成员运算符。对成员变量

10、可以象普通变量一样进行各种操作。例如,将学号11301赋给student1中的num,应写成以下形式:student1.num=11301;将姓名“ZhangPing”通过键盘赋给student1中的name,应写成:scanf("%s",&);将student2中的score加1,然后输出该值,应写成:student2.score=student2.score+1; 或student2.score+;printf("%f",student2.score);成员运算符的运算级别最高,例如:student.num+100,

11、在num两侧有二个运算符,由于成员运算符的运算优先于加号运算符,故相当于(student.num)+1002.成员本身又是结构体类型时的子成员的访问如果成员本身又是一种结构体类型时,那么对其下级子成员再通过成员运算符去访问,一级一级地直到最后一级成员为止。例如上文提到的birthday,可以这样去访问:student1.numstudent1.birthday.yearstudent1.birthday.monthstudent1.birthday.daystudent1.score这里,student1.birthday本身相当于一个结构体变量。注意下述用法是错误的:year /*少了上两级

12、所属主体*/birthday.year/*少了结构体变量主体*/student1.year/*不能跨级访问*/year.birthday.student1/*不能颠倒次序*/3.同一种类型的结构体变量之间可直接赋值一般地,可以将一个结构体变量作为一个整体赋给另一个具有相同类型的结构体变量。例如:student2=student1;student1与student2两者类型相同,上述赋值语句相当于将student1中各个成员的值逐个依次赋给student2中的相应成员。若两者的类型不一致时,则不能直接赋值。通常,也可以把一个结构体变量中的内嵌结构体类型成员赋给同种类型的另一个结构体变量的相应部分

13、。如下列语句是合法的:student2.birthday=student1.birthday;4.不允许将一个结构体变量作为一个整体进行输入或输出下述用法是错误的:scanf("%d,%s,%c,%d,%f",&student1);/*错*/printf(" %d",student1); /*错*/printf("%d,%s,%c,%d,%f",student1); /*错*/5.一个结构体变量所占用的存储空间就是其所有成员所占空间之和。11.1.3       结构

14、体变量的初始化与其他类型变量一样,对结构体变量也可以在定义时进行初始化赋值,但附在变量后面的一组数据须用花括号括起来,其顺序应与结构体中的成员顺序保持一致。【例11-1】对结构体变量初始化。main() struct student int num;char name20;char sex;int age;float score; student1 =11301,"Zhang Ping",'F',19,496.5;printf("Number=%dnName=%sn",student1.num,);printf(

15、"Score=%fn",student1.score);运行结果如下:Number=11301Name=Zhang PingScore=496.500000本例中,student1在被定义的同时,其各成员也按顺序被赋予了相应的一组数据。11.2 结构体数组一个结构体变量只能存放一个对象(如一个学生、一个职工)的一组数据。如果要存放一个班(30人)学生的有关数据就要设30个结构体变量,例如student1,student2,student30,显然是不方便的。人们自然想到使用数组。C语言允许使用结构体数组,即数组中每一个元素都是一个结构体变量。11.2.1 结构体数组的定义定

16、义结构体数组的方法与定义结构体变量方法相似,只是要多用一个方括弧以说明它是个数组。在上一节中定义结构体变量的三种方法可以作为定义结构体数组的参考。如:struct student int num; char name20; char sex; int age; float score; student1,stu30;以上定义了一个结构体变量student1和一个结构体数组stu,这个数组有30个元素,每一个元素都是struct student类型的,如图11-4所示。数组各元素在内存中占用连续的一段存储单元。numnamesexagescorestu011301Zhang PingF19496

17、.5stu111302Wang LiF20483stu2911330Mao QiangM18502图11-4结构体数组定义之后,要引用某一元素中的一个成员,可采用以下形式:stui.score式中i为数组元素的下标。11.2.2 结构体数组的初始化只有对定义为外部的或静态的数组才能初始化。在对结构体变量初始化时,要将每个元素的数据分别用花括弧括起来。【例11-2】设有四位同学的有关数据,试统计出他们的平均年龄和平均成绩。struct student int num; char name20; char sex; int age; float score;struct student stu4=

18、 11301,"Zhang Ping",'F',19,496.5 ,11302," Wang Li ",'F',20,483,11303,"Liu Hong",'M',19,503,11304,"Song Rui",'M',19,471.5;main() int i;float a,s;for (i=0;i<4;i+) a=a+stui.age;s=s+stui.score; printf("The average age is %6.

19、2fn",a/4);printf("The average score is %6.2fn",s/4);运行结果如下:The average age is 19.25The average score is 488.5011.3 结构体指针变量通常,可以定义一个指针变量用来指向一个结构体变量,这就是结构体指针变量。结构体指针变量的值就是所指结构体变量在内存单元中的起始地址。指针变量也可用来指向结构体数组中的元素。11.3.1 结构体指针变量定义结构体指针变量的一般形式如下:struct student *p;上述语句定义了一个指针变量p,它可以指向任何一个属于st

20、ruct student类型的数据。通过指针去访问所指结构体变量的某个成员时,有如下两种方法:(*p).score或者p->score后者是常见的一种使用方式,其中->称为指向运算符。【例11-3】 用指针访问结构体变量及结构体数组struct student int num; char name20; char sex; int age; float score;struct student stu3=11302,"Wang",'F',20,483,11303,"Liu",'M',19,503,11304,&

21、quot;Song",'M',19,471.5;main() struct student student1=11301,"Zhang Ping",'F',19,496.5,*p,*q;int i;p=&student1;/* 让指针p指向结构体变量student1 ,如图11-5所示*/printf("%s,%c,%5.1fn",,(*p).sex,p->score);q=stu;/*让指针p指向数组stu,即指向数组中的第一个元素stu0,如图11-6所示 */for

22、( i=0;i<3;i+,q+) printf("%s,%c,%5.1fn",q->name,q->sex,q->score);运行结果如下:Zhang Ping,F,496.5Wang,F,483.0Liu,M,503.0Song,M,471.5指针符号“->”的使用比较常见。请分析以下几种运算:p->age得到p指向的结构体变量中的成员age的值;p->age+先引用p所指成员age的值,用完后再使该成员值加1;+p->age先使p所指成员age的值加1,然后再引用这个新值;(p+)->age先引用p->age

23、的值,用完后再使指针p加1;(+p)->age先使指针p加1,然后再引用p->age这个值;例如,【例11-3】中的for语句:for ( i=0;i<3;i+,q+) printf("%s,%c,%5.1fn",q->name,q->sex,q->score);可改写为:for ( i=0;i<3;i+) printf("%s,%c,%5.1fn",q->name,q->sex,(q+)->score);11.3.2 用结构体变量和结构体指针变量作函数参数结构体变量以及结构体指针变量均可以象i

24、nt类型那样作为函数的参数,甚至可以把一个函数定义成结构体型或结构体指针型。【例11-4】对年龄在19岁以下(含19岁)同学的成绩增加10分。struct student int num; char name20; char sex; int age; float score;struct student stu3=11302,"Wang",'F',20,483,11303,"Liu",'M',19,503,11304,"Song",'M',19,471.5;void print(str

25、uct student s) printf("%s,%d,%5.1fn",,s.age,s.score);void add10(struct student *q) if (q->age<=19) q->score=q->score+10;main() struct student *p;int i;for (i=0;i<3;i+) print(stui);/*调用print函数*/for (i=0,p=stu;i<3;i+,p+) add10(p);/*调用add10函数*/printf("n");for

26、 (i=0,p=stu;i<3;i+,p+) print(*p);运行结果如下:Wang,20,483.0Liu,19,503.0Song,19,471.5Wang,20,483.0Liu,19,513.0Song,19,481.5本例中,函数print中的形参s,属于struct student结构体类型,与调用语句中的实参stui或*p的类型一致;函数add10中的形参q,属于struct student结构体指针类型,与调用语句中实参p指针的类型一致。【例11-5】将上例中的函数add10改写成一个返回结构体类型值的函数struct student add10(struct stu

27、dent *q) if (q->age<=19) q->score=q->score+10;return *q;相应地,在主函数main中的调用语句也须改为:for (i=0,p=stu;i<3;i+,p+) *p=add10(p);11.4链表11.4.1链表概述通过前面有关章节的学习,我们知道,用数组存放数据时,必须事先定义固定的长度。比如a100最多可以存放100个数组元素,多一个都不行。如果待处理的数据个数较多,事先难以确定数组的大小时,我们只能把这个数组定义得足够大,以存放任何可能具有的数据。由于数组的大小与其占用的内存空间成正比,因此,在这种情况下内存

28、浪费现象将比较严重。为此,我们需要一种新的数据结构:当数据每增加一个时,可以向系统申请空间从中增加一个元素;当数据每减少一个时,可以删除一个元素,释放其所占内存空间。也就是能够进行动态存储分配,这就是链表。链表是一种常见的重要的数据结构。图11-7链表中的每个元素称为结点,一个链表由若干结点组成。图11-7是一种简单的单向链表。链表中的每个结点都应包括两部分:其一是数据区,存放本结点要存储的数据,其二是指针区,存放下一个结点的首地址。因此,链表中的每个结点在内存中可以是不连续的。访问链表时,通过第一个结点,可以找到第二个结点;通过第二个结点,又可以找到第三个结点;直到最后一个结点为止。链表的第

29、一个结点称为头结点,最后一个结点称为尾结点。链表的这种数据结构只能利用指针变量才能实现。指向头结点的指针变量称为链表的头指针,图11-7中以head表示。尾结点因无需指向其它结点,通常将其指针区赋值为空值NULL,以便判断。11.4.2 处理链表的函数在对链表进行动态管理时,需要用到如下的几个函数。:1. 分配内存空间函数malloc调用形式: (类型说明符*) malloc(size)功能:在内存的动态存储区中分配一块长度为size个字节的连续区域。函数的返回值为该区域的首地址。“类型说明符”表示指定该区域用于何种数据类型,“(类型说明符*)”表示把返回值强制转换为该类型指针,“size”用

30、于指定空间大小。例如: pc=(char *)malloc(100);表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的首地址,上述语句把该地址赋予指针变量pc。2. 分配内存空间函数 calloccalloc 也用于分配内存空间。调用形式: (类型说明符*)calloc(n,size)功能:在内存动态存储区中分配n块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。calloc函数与malloc 函数的区别仅在于一次可以分配n块区域。3. 释放内存空间函数free调用形式: free( 指针变量ptr);功能:释放ptr所指向的一

31、块内存空间,ptr是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由malloc或calloc函数所分配的区域。除了上述三个函数外,另有一个虽与链表没有直接关系,但在创建结点中经常用到的函数sizeof,它用于测试某种数据类型的宽度,也就是这种类型的数据在内存中所占用的字节数。例如:float a;printf("%d",sizeof(char);printf("%d",sizeof(a);结果为1和4,因为一个字符型数据只需占用一个字节,一个实型变量占四个字节。ANSI C标准要求在使用动态分配函数时要用#include命令将stdli

32、b.h文件包含进来。11.4.3 链表的建立要建立链表,必须先定义结点的数据类型。前面介绍的结构体变量,包含若干成员。这些成员可以是数值类型、字符类型、数组类型,也可以是指针类型。这个指针类型可以指向其它结构体类型,也可以指向自身所在的结构体类型。链表中结点的数据类型正是根据后面这个思路来定义的。如:struct student int num; char name20; char sex; int age; float score; struct student *link;link是成员名,属于指针类型,它指向struct student类型数据,用于存放下一个同类型结点的首地址。link

33、成员给链表中各个结点的连接提供了可能。比如,现有两个指针p、q,分别指着两个独立结点,另有一个结构体变量stud,通过如下程序片段就可将它们串成一个链表,如图11-8所示。图11-8struct student *head,*p,*q, stud;p=(struct student *)malloc(sizeof(struct student); /*创建第一个结点,p指向它*/p->num=1; p->score=483;/*将数据置入该结点中*/q=(struct student *)malloc(sizeof(struct student);/*创建第二个结点,q指向它*/

34、q->num=2; q->score=503;stud.num=3; stud.score=471; head=p;/* 设head指向第一个结点,作为头指针 */p->link=q; /* 在p所指结点的后面接上第二个结点 */q->link=&stud;/* 在q所指结点的后面接上变量stud所表示的结点 */stud.link=NULL;/* 设置空指针,尾结点不再指向其它结点 */注意最后两句,stud是一个结构体变量,引用时与指针变量是有区别的。【例11-6】以-1作为结束标志,编写一个创建链表的函数#define NULL 0#include &qu

35、ot;stdlib.h"struct student int num; /*学号*/ float score; /*成绩*/ struct student *link; /*指向下一个结点的指针*/;struct student *creat() struct student *head,*p,*q; int number; head=NULL; /*初始为空链表,没有任何结点*/ scanf("%d",&number); /*事先读入一个学号*/ while (number!=-1) /* 若不是结束标志-1,则通过以下循环创建一个结点*/ q=(str

36、uct student *)malloc(sizeof(struct student); /*申请一个新的结点*/ q->num=number; /*将先前读入的学号放入到该结点中*/ scanf("%f",&q->score); /*再读入该结点的其他数据*/ if (head=NULL) /*刚才新建的是不是第一个结点*/ head=q; /*是,则令该结点为头结点,head为头指针*/ else p->link=q; /*否,则将该结点挂到链表尾部*/ p=q; /*p总是指向已建链表的最后一个结点*/ scanf("%d"

37、;,&number); /*读入下一个学号*/ if (head!=NULL) p->link=NULL; /*如果链表不为空,则设立尾结点标志*/ return(head); /*返回新建链表的头指针*/ creat函数用于建立一个新的链表,它是一个指针函数,它返回的指针属于struct student类型,指向新链表的头结点。在creat函数内定义了三个struct student类型的指针变量,其中head作为头指针,总是指向第一个结点;每次在表尾添入一个结点后,p总是用来指向最新的尾结点;q用来申请新的结点。11.4.4 链表的遍历相对于链表的创建而言,链表的遍历,也就是

38、对链表的每一个结点访问一遍,是比较容易的。遍历一个链表的技术要点有三:一是要从头结点开始,因为单向链表反向访问是不便的;二是每访问一个结点前,必须先判空,防止过了表尾;三是当前结点访问后,需令指向当前结点的指针指向下一个结点,以利程序循环操作。【例11-7】编写一个遍历链表的函数。void print(struct student *phead)/*要求将一个链表的头指针作为参数传入*/ struct student *p; p=phead;/*从头结点开始*/ while (p!=NULL)/*当前结点若不为空,则继续访问*/ printf("%d,%5.1fn",p-&

39、gt;num ,p->score ); p=p->link ;/*指向下一个结点*/ main() print(creat();/*先调用函数creat,其返回值作为头指针再传给函数print */将【例11-6】与【例11-7】的程序段合在一起,便是一个完整的链表输入输出程序。11.4.5 链表的插入操作链表的插入操作就是将新的结点插入到一个现有的链表中。插入的基本思想是:如果要在原来相邻的两个结点a和b之间插入一个新的结点c,则需把结点a中的指针指向c,把结点c中的指针指向b,这样就由原来的ab链变成了acb,而排在a之前的结点与b之后的结点都不受影响。插入操作可分为四种情形:

40、 在一个空链表中插入; 插在一个链表的头结点之前; 插在两个结点之间; 插在尾结点之后。前两种情形插入后需要改变链表的头指针。图11-9(a)为插入前的情形;图11-9(b)为插入在头结点之前的结果;图11-9(c)为插入在两个结点之间的结果。图11-9(a)图11-9(b)图11-9(c)由图11-9(a)到图11-9(b)的变化,可通过如下两条语句实现:p0->link=head;head=p0;注意,这两条语句的前后次序不能颠倒。因为一旦先失去了head中原先的值,就再也没法将原先head所引导的链表接回来了。好比您要将手中正在放着的风筝交给别人,这根牵着的线,是等到别人接上手之后

41、您再松手,还是您先松手了然后别人再过来接?同理,由图11-9(a)到图11-9(c)的变化,可通过如下语句实现:p=p->link;p0->link=p->link;p->link=p0;先通过一条或多条p=p->link这类语句,向后逐步寻找插入点,然后再实施有关的链接操作。【例11-8】写一个插入函数,在一个有序的链表中插入一个结点,要求插入后的链表依然有序。本例中,以学号为关键字确定各结点的前后顺序。struct student *insert(struct student *phead,struct student *p0) struct student

42、*p,*q; if (phead=NULL ) phead=p0;p0->link=NULL; /*在空链表中插入*/ else q=NULL; p=phead; /*从头结点开始往后一步一步寻找插入点*/ while(p0->num>=p->num && p->link !=NULL) q=p; p=p->link; /*q指向当前结点,p指向下一个结点 */ if (q=NULL) p0->link=phead; phead=p0; /*插到首结点之前*/ else if (p0->num<p->num) p0-&

43、gt;link=p; q->link=p0; /*插到q与p所指向的结点之间*/ else p->link=p0; p0->link=NULL; /*插到尾结点之后*/ return(phead);11.4.6 链表的删除操作链表的删除操作就是删除现有链表中的某个结点。删除的基本思想是:如果原来的链接关系是abc,要把b结点删除,则需把结点a中的指针指向c,把结点b所占的内存空间释放,这样就由原来的abc链变成了ac,而排在a之前的结点与c之后的结点都不受影响。删除一个结点时,需要使用free函数释放其所占空间。删除操作可分四种情形: 对一个空链表操作; 要删除的是链表的头结

44、点,这种情形需要改变链表的头指针; 删除其它的结点,链表的头指针不动; 拟删除的结点在链表中不存在。【例11-9】写一个删除函数,删除链表中指定学号所在的结点。并结合创建函数、遍历函数、插入函数的调用,给出一个main主函数。struct student *del(struct student *phead,int num0) struct student *p,*q; p=phead; if (phead=NULL) /*是不是一个空链表?*/ return(phead); else if (phead->num=num0) phead=phead->link; /*要删除的是头

45、结点,把下一个结点作为新的头结点*/ else while(p->num!=num0 ) /*根据关键字num0查找结点*/ q=p; p=p->link; /* q指向当前结点,p指向下一个结点 */ if (p=NULL) break; /* 查找完毕,不再查找 */ /* 循环完成后,p指向要删除的结点 */ if (p!=NULL) q->link=p->link; /*删除p所指向的结点*/ else return(phead); /*未找到要删除的结点*/ if (p!=NULL) free(p); /*释放空间*/ return(phead);main()

46、 struct student *head,*newnode; int num; head=creat();/*创建一个链表*/ printf("before insert:n"); print(head);/*在插入前输出链表*/ printf("input the inserted record:"); newnode=(struct student *)malloc(sizeof(struct student); scanf("%d,%f",&newnode->num,&newnode->score)

47、; head=insert(head,newnode);/*插入一个结点*/ printf("after insert:n"); print(head);/*在插入后输出链表*/ printf("input the num to delete:"); scanf("%d",&num); head=del(head,num);/*删除链表中的一个结点*/ printf("after delete:n"); print(head);/*在删除一个结后再输出链表*/说明:完整的程序由例11-6中的结点类型定义与c

48、reat创建链表函数、例11-7中的print遍历函数、例11-8中的insert插入函数以及上述del删除函数与main主函数组成。程序运行情况如下,其中“-1,-1”这一行之前的数据由键盘输入。23,48331,50135,493-1,-1before insert:23,483.031,501.035,493.0input the inserted record:27,450after insert:23,483.027,450.031,501.035,493.0input the num to delete:31after delete:23,483.027,450.035,493.0

49、11.5 联 合 体11.5.1联合体类型定义所谓联合体数据类型是指将不同的数据项存放于同一段内存单元的一种构造数据类型。同结构体类型相似,在一个联合体内可以定义多种不同的数据类型;不同的是,在一个联合体类型的变量中,其所有成员共用同一块内存单元,因此,虽然每一个成员均可以被赋值,但只有最后一次赋进去的成员值能够保存下来,而先前赋进去的那些成员值均被后来的覆盖了。定义一个联合体类型的一般形式为:union 联合体名 成员1类型1;成员2 类型2;.成员n 类型n;例如:union data int a; float b; charc;union data x,y;也可以将类型定义与变量定义合在

50、一起:union dataint a;float b;char c; x,y;联合体与结构体虽形式相似,但含义有别。一个结构体变量所占内存长度是各成员占的内存长度之和,每个成员分别占有自己的内存单元;而一个联合体变量所占内存长度等于其所有成员中最长的成员的长度,所有成员共用一段内存单元,所以,有的地方也把联合体称为共用体。11.5.2联合体变量的引用对联合体变量的赋值、使用都只能是对变量的成员进行。联合体变量的成员表示为:联合变量名.成员名例如,对于上文定义的变量x与y,可使用以下三种方式之一访问成员值。x.a或者x.b或者y.c在使用联合体类型数据时应注意以下一些特点: 同一内存段可以用来存

51、放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种。也就是说,每一瞬时只有一个成员起作用,其它的成员不起作用,即不是同时都存在或起作用。 联合体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。例如,以下几条赋值语句:x.a=1;x.b=3.6;x.c=H;虽然先后给三个成员赋了值,但只有x.c是有效的,而x.a与x.b已经无意义而且也不能被引用了。 联合体变量的地址和它的各成员的地址都是同一地址。 不能对联合体变量名赋值,也不能企图引用变量名来得到成员的值,又不能在定义联合体变量时对它初始化。例如,下列语句都是错误的:union data

52、int a;float b;char c; x=1,3.6,H,y; /*错,不能初始化 */x=1;/*错,不能对联合体变量名赋值*/y=x; /*错,不能引用联合体变量名以得到值*/ 不能把联合体变量作为函数参数,也不能把一个函数的类型定义成联合体类型,但可以使用指向联合体变量的指针。 联合体与结构体可以互相嵌套。在联合体中可以定义结构体成员,或者也可以在结构体中定义联合体成员。【例11-10】一个学校的人员数据管理中,教师的数据包括:编号、姓名、性别、职务,学生的数据包括:编号、姓名、性别、班号。它们放在同一种表格中,显然有这么一栏,或者登记教师的“职务”,或者登记学生的“班号”,而不会

53、在这同一栏中同时写上这两项数据。试给出类型定义及输入输出方法。编号num姓名name性别sex职业job班号class职务positionstruct person long num; char name20; char sex; char job;union int class; char position20;category;person10;结构体成员job用作身份标志,如果输入为s(学生),则要对联合体成员category中的class操作,如果输入为t,则要对其中的position操作。输入输出方法为:scanf(" %c",&person0.job);

54、if (person0.job=s) scanf("%d",&person0.category.class);else scanf("%s",&person0.category.position);if (person0.job=s) printf("%d",person0.category.class);else printf("%s",person0.category.positon);11.6 枚 举 类 型所谓“枚举”类型,是指这种类型变量的取值只能限于事前已经一一列举出来的值的范围。比如描述星期几的数据就只能在星期日、星期一到星期六之间选择。用关键字enum定义枚举类型,如:enum weekday sun, mon, tue, wed, thu, fri, sat;weekday是枚举类型名,可以用于定义变量,如:enum weekday week1, week2;定义

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论