程序设计与C语言第8章结构体、共用体及枚举类型.ppt

上传人:小飞机 文档编号:6482005 上传时间:2023-11-04 格式:PPT 页数:145 大小:208.50KB
返回 下载 相关 举报
程序设计与C语言第8章结构体、共用体及枚举类型.ppt_第1页
第1页 / 共145页
程序设计与C语言第8章结构体、共用体及枚举类型.ppt_第2页
第2页 / 共145页
程序设计与C语言第8章结构体、共用体及枚举类型.ppt_第3页
第3页 / 共145页
程序设计与C语言第8章结构体、共用体及枚举类型.ppt_第4页
第4页 / 共145页
程序设计与C语言第8章结构体、共用体及枚举类型.ppt_第5页
第5页 / 共145页
点击查看更多>>
资源描述

《程序设计与C语言第8章结构体、共用体及枚举类型.ppt》由会员分享,可在线阅读,更多相关《程序设计与C语言第8章结构体、共用体及枚举类型.ppt(145页珍藏版)》请在三一办公上搜索。

1、第8章 结构体、共用体及枚举类型,8.1 结构体类型8.2 动态数据结构8.3 共用体8.4 位段8.5 枚举类型,8.1 结构体类型,8.1.1 结构体变量的定义及初始化 1.结构体类型的定义 要定义结构体变量,首先要定义结构体类型。结构体类型定义的一般形式是:struct;;=;=,其中,符号“=”表示“定义为”,方括号中的内容是可选的。例如语句 struct date int year;int month;int day;,就定义了一个表示日期的结构体类型,类型名为struct date。它的三个分量的类型均为整型。对结构体来说,分量的类型可以互不相同,这是它与数组的区别。数组也是构造类

2、型,但要求其元素具有相同的类型。再如 struct student unsigned num;char name10;int age;float score;,(1)两个结构体变量的定义是分离的,后者可以把前者作为其分量类型。比如上面已经定义了日期类型,则可以用它来定义学生类型:struct student unsigned num;char name10;int age;float score;struct date birthday;,(2)还可以在一个结构体内部直接嵌套定义:Struct studentunsigned num;char name10;int age;float scor

3、e;structint year;int month;int day;birthday;,2.结构体变量的定义 在定义了类型名之后,就可以定义该类型的变量了。定义结构体变量的方法有三种:(1)如结构体类型已定义好,则可以用来定义变量。如:struct date date1,date2;struct student stu1,stu2;注意:在使用结构体类型名时,初学者往往会忽略保留字struct,其实struct date和struct student都是一个统一的整体,二者缺一不可。,(2)定义结构类型的同时直接定义变量,如 struct date int year;int month;in

4、t day;date1,date2;(3)定义结构体类型的同时定义变量,但没有结构名。,3.结构体变量赋初值 结构体变量可以在定义时赋初值,如语句 structst udent stu1,stu2=63001,zhang,18,642.5;就对结构体变量stu2进行了初始化,实际上是用右边的值对stu2各分量进行初始化,因此提供的初值必须和相应分量的类型一致。两个相同类型的结构体变量之间可以进行赋值操作。如:stu1=stu2;则使stu1的各分量具有了和stu2各分量一样的值。,4.结构体指针变量的定义 除了定义结构体变量之外,还可定义结构体指针变量。如:struct student stu

5、1,*p;p是结构体类型指针。像其它类型的指针一样,结构体指针只有和某个结构体变量发生了联系,即得到了结构体变量的首地址之后才能被使用。如 p=这样就把stu1的首地址,即第一个分量的地址赋给了p,p于是指向这个结构体变量。,8.1.2 结构体数组及结构体分量的引用 1.结构体数组 一个结构体变量可以处理一个对象,如果有多个对象,则需要多个结构体变量,这时应该用结构体数组来处理多个同类型的对象。例如,定义一个产品类型的数组prod:struct product unsigned long no;char name15;int num;float price;prod3;,定义结构体变量的其它两

6、种方法也可以用来定义结构体数组。对结构体数组可以初始化,例如 struct student prod3=112346,football,56,284.5,112347,basketball,108,256,112348,valleyball,35,96.4;,图81 结构体数组的逻辑结构,2.对结构体分量的引用 对结构体分量的引用有三种方法:用点运算符引用法;用指向运算符引用法;对数组元素的分量用下标加点或指向运算符引用法。下面分别加以说明。(1)用点运算符引用结构体变量的分量的方法,有两种引用形式:.(*).,即在结构体变量和其分量名之间加一个点运算符。例如:struct product p

7、rod,*p=*/strcpy(prod.name,football);这是对结构体变量prod的分量进行赋值的运算。这时prod.num(或(*p).num)、prod.name作为一个独立的变量使用,可以直接进行输入/输出操作。,【例81】includestruct productunsigned long no;char name15;int num;float price;,main()struct product prod;prod.no=117364;prod.num=46;prod.price=287.5scanf(%s,prod.name);printf(%lu,%s,%d,%

8、fn,prod.no,prod.name,prod.num,prod.price);return 0;,运行输出:football 117364,football,46,287.5 注意:name分量的输入,因它是字符数组,所以不能用赋值语句直接赋值,只能用字符串处理函数strcpy或用scanf函数的控制符“%s”控制输入。,(2)用指向运算符引用结构体指针所指对象的分量的方法,是用结构体指针处理结构体的常用形式,其引用形式为:-指向运算符由两个字符“-”和“”组成,它是一个整体,中间没有空格。例如:p-no=117368;strcpy(p-name,basketball);,【例82】用指

9、针重做例81。includestruct productunsigned long no;char name15;int num;float price;main()struct product prod,*p;,p=运行输出:117364 valleyball 75 197.6117364,valleyball,75,197.6,(3)用下标加点运算符引用结构体数组元素的分量的方法,其引用形式为:.注意:下标和数组名紧密相连,不可分离,不能把下标放在分量名后面。例如:struct product prod;则 prod2.price=78.5;是正确的引用,而 prod.price则是错误的

10、写法。,【例83】建立和输出有3个元素的产品结构体数组,并输出价格最高的产品的所有信息。include struct product unsigned long no;char name15;int num;float price;struct int year,month,day;outdate;prod3;,main()int i,j;float max=0;struct product*p=prod;puts(Input prod3:n);for(i=0;i=2;i+)scanf(%lu%s%d%f%d%d%d,printf(the prodarray is:n);for(i=0;ino

11、,p-name,p-num,p-price,p-outdate.year,p-outdate.month,p-outdate.day);p+;printf(n);for(i=0;imax),max=prodi.price;j=i;printf(Element having highest price:n);printf(%lu,%s,%d,%.2f,%d,%d,%dn,prodj.no,prodj.name,prodj.num,prodj.price,prodj.outdate.year,prodj.outdate.month,prodj.outdate.day);return 0;,运行输出

12、:Input prod3:11111 aaaaa 89 99.2 2001 11 922222 bbbbb 76 100.4 2002 1 833333 ccccc 96 187 2002 8 4The prod array is:11111 aaaaa 89 99.20 2001/11/922222 bbbbb 76 100.40 2002/1/833333 ccccc 96 187.00 2002/8/4Elements having highest price:33333 ccccc 96 187.00 2002/8/4,(4)用指向运算符也可以引用结构体数组元素的分量,因为对结构体数组

13、可以用指针进行处理,令 p=prod;则p指向数组prod的第1个元素,p+指向下一个元素,每个元素又是结构体,仍要用“-”运算符求其分量。如图82所示。,图82 结构体数组的指针处理,【例84】分析下面程序的输出结果。includestruct sinlchar*s;inti;struct sinl*slp;main()static struct sinl a=“abcd,1,a+1,“efgh,2,a+2,“ijkl,3,a;struct sinl*p=a;int i;printf(a0.s=%stp-s=%sta2.slp-s=%snn,a0.s,p-s,a2.slp-s);for(i=

14、0;i2;i+)printf(-a%d.i=%dt+a%d.s3=%cn,i,-ai.i,i,+ai.s3);,printf(“+(p-s)=%sta(+p)-i.s=%st“a-(p-slp-i).s=%sn,+(p-s),a(+p)-i.s,a-(p-slp-i).s);return 0;运行输出:A0.s=abcd p-s=abcd a2.slp-s=abcd-a0.i=0+a0.s3=e-a1.i=1+a1.s3=i+(p-s)=bce a(+p)-i.s=efgh a-(p-slp-i).s=ijkl,图83 结构体中指针的指向,8.1.3 结构体变量作参数 结构体变量和结构体指针都

15、可以作为函数的参数及函数的返回值。若形参为结构体变量,实参也为结构体变量时,则参数传递的是结构体的拷贝,属于函数的传值调用。但这样做既费时间又费空间。如果把形参定义成指针类型,就可以解决这两方面的问题。这样实参传递的是结构体变量的地址,函数中对形参的处理就是对实参的直接处理。下面的例子说明了这种情况。,【例85】求两个复数的和与积。编程思路:复数相加的公式为:(a+bi)+(c+di)=(a+c)+(b+d)i 即两个复数相加结果仍为一复数,结果的实部为原两个复数的实部之和,结果的虚部为原两个复数的虚部之和。复数相乘的公式为:(a+bi)*(c+di)=(ac-bd)+(bc+ad)i 复数可

16、以设计成一个结构体类型。复数相加与相乘用两个函数表示。复数结构体类型在各个函数中都要使用,因此把它放到所有函数之外,作为全局类型定义。,8.1.4 类型名定义typedef 在上面的例子中,我们定义了一个复数类型 struct complex,这是个不能分开的整体,利用这个类型名可以定义变量、函数值等。但这个类型名很长,稍不留心就会出错。如果程序中有很多的复数类型的变量和函数需要定义,书写起来更是不胜其烦。能不能简单一些呢?答案是肯定的。C语言提供了一种机制,利用保留字typedef就可以用一个简单的名字来代替像 struct complex这样的长序列。,例如:typedef struct

17、complex COMPLEX;则 COMPLEX 即是和struct complex等价的类型名,可以用它定义变量:COMPLEX a,b,c,*pa;这样用起来就方便多了。用typedef说明类型名的一般形式如下:typedef 新类型名一般用大写表示,以便与原名在性质上区别开来,即它不是新创造的类型,而是原类型的代名词或化身,它代表了原类型。,说明新类型的方法十分简单,可按下列步骤进行:(1)先定义原类型的变量;(2)把变量名大写,以示它为新类型名;(3)在前面加上typedef保留字。,8.2 动态数据结构,到目前为止我们所使用的数据结构如数组等,其大小是一开始就定义好的,程序中不能改

18、动,这对内存的合理使用及某些操作非常不便。而动态数据结构是一开始并不指定大小,可以随需要不断增加,不用时随时取消的结构,如链表、堆栈、队列、树等,这些动态结构在信息科学中非常有用。,8.2.1 动态分配内存 建立和维护动态数据结构需要进行动态的内存分配,即在程序执行过程中可以动态地得到内存和回收内存。动态分配内存的极限是计算机中可用的物理内存空间。为实现动态分配内存,C语言提供了几个专用的函数和运算符,它们是函数malloc、calloc、free和运算符sizeof。,1.malloc函数 该函数原型为:void*malloc(unsigned size)功能:在内存中开辟size个字节大小

19、的连续空间。返回值为该空间的起始地址。若分配不成功,则返回0值。2.calloc函数 calloc函数的原型为:void*calloc(unsigned n,unsigned size);功能:在内存中开辟n个大小为size个字节(共n*size字节)的连续空间。返回值为该段空间的起始地址。若分配不成功,则返回0值。,图84 返回指针的指向,【例86】编一函数strsave,它可接收一个字符串,然后动态地开辟一个能放得下这个字符串的内存空间,把接收到的字符串复制到其中,并返回该空间的起始地址。include include include char*strsave(char*);main(),

20、char*str=China,*cp;cp=strsave(srt);printf(str=%s,cp=%sn,str,cp);return 0;char*strsave(char*s)char*p;if(p=(char*)calloc(strlen(s)+1,1)!=NULL)strcpy(p,s);return p;,运行输出:str=China cp=China 标准函数calloc分配strlen(s)+1个大小为1的内存空间,这是因为strlen函数统计字符串长度时不包含0字符,但在复制字符串中还需要把0加进去,所以分配空间时要加1。原calloc函数返回的是空类型指针,现在把它强制

21、转换成char型指针,把该指针值赋给p,再判断p是否为NULL(0),若不为NULL,就调用字符串拷贝函数来完成拷贝工作。,8.2.2 链表 链表是用链表指针连在一起的自引用结构(称为“结点”)的线性集合,如图85所示。其中,head是指向链表第一个结点的指针,通过它来访问链表。后面的结点是通过它前面结点中的链接指针来访问的。链表的最后一个结点中的链接指针被置为NULL(画成反斜杠以表示链尾)。链表中的结点是在需要时建立的,链表中的数据是动态存储的。,图85 链表的结构,我们可以把动态的链表和静态的数组作一对比,以说明它们的特点。(1)在数据元素的个数不可预知时,使用链表是合适的,因为可在需要

22、时增加或减少链表中结点的个数;数组是在编译时分配内存的,其大小是不可改变的。(2)当数组定义得很大以备不时之需时会造成空间的浪费;链表随用随增,不会造成空间的浪费。,(3)对插入和删除操作,用数组费时费力,而链表可以方便地在合适的位置插入和删除结点,只要把有关结点的链接指针修改一下即可。(4)数组中的元素在内存中是连续存放的,根据相对于数组的起始位置可以计算出数组元素的地址,所以可以立即访问到任意位置的数组元素;链表中的结点不能被立即访问到,因为链表中的结点在逻辑上是连续的,但在内存中是不连续的。,下面我们研究链表的建立、插入、删除及输出等操作。1.建立链表 首先定义链表中结点的类型,它应该是

23、个自引用的结构体。如 struct node int data;struct node*next;typedef struct node Node;Node为新类型名。,先建立只有一个结点的链表,使head、p1、p2都指向它,如图86所示。其操作步骤如下:(1)产生一个结点,用p1指向它:p1=(Node*)malloc(sizeof(Node);(2)把p1赋给head和p2:p2=head=p1;(3)对新结点的数据域输入数据,而把其指针域置为NULL。,图86 链表的建立,图87 建立两个结点的链表,2.输出链表 当链表的头指针为NULL时说明链表是空的,不采取行动。只有当链表非空时才

24、从链表头部开始,输出一个结点的值,然后移动指针,再输出下一个结点的值,直至表尾结束。可用下面的函数完成此项功能。void printList(Node*h)Node*p;p=h;if(h=NULL),printf(list is empty!nn);else while(p!=NULL)printf(%d-,p-data);p=p-next;printf(NULLnn);,3.插入结点 在一个链表中插入结点,首先要确定插入的位置,这里要考虑几种情况:(1)空表情况;(2)插在表头;(3)插在表中;(4)插在表尾。,(1)结点插在表头:设原头结点数值为8,把数值为6的结点插入其中,如图88所示。

25、,图88 结点插入表头,(2)结点插在表中间:在上表的基础上插入数值为9的结点,如图89所示。,图89 结点插入表中间,(3)结点插在表尾:在上表基础上插入数值为16的结点,如图810所示。插在表尾,意味着在while循环中以p1=NULL为条件而退出循环。,图810 结点插在表尾,4.删除结点从一个链表中删除结点也应考虑几种情况:(1)删除表头结点;(2)删除表中或表尾结点;(3)找不到要删的结点。,完成该功能的函数中使用了三个工作指针:p1指向当前考查结点,p2指向当前结点的前一结点,temp指向被删结点。函数如下:Node*delete(Node*h,int value)Node*p1,

26、*p2,*temp;if(value=h-data)/*删除表头结点*/temp=h;h=h-next;/*解除表头与链表的连接*/free(temp);/*释放该结点的内存*/,else p2=h;p1=h-next;while(p1!=NULL,else printf(%d not foundn,value);return h;,图811 删除表头结点,我们以图示来说明删除结点的操作。(1)删除表头结点:删除数值为8的结点,如图811所示。(2)删除中间结点:删除数值为12的结点,如图812所示。(3)删除表尾结点:删除数值为16的结点,如图813所示。,图812 删除中间结点,图813

27、删除表尾结点,【例87】includeincludestruct node int data;struct node*next;typedef struct node Node;main()Node*head1,*head2;int i;head1=create();,printf(The list is follows!n);printList(head1);printf(Input a integer:n);scanf(%d,printf(Input a integer to delete:n);scanf(%d,【例88】指出下面程序的运行结果。includeincludetypedef

28、 struct node int d;struct node*next;t-node;int t=0;void create(t-node*h)int i;t-node*p;,scanf(%d,main()t-node*h=NULL,*p;puts(n Input integer,0 to end:);create(,运行输出:Input integer,0 to end:22 33 44 0The output is:22 55 77,该程序中定义了一个全局变量t,将它赋初值为0,在函数调用过程中t的值在不断地变化。函数create用以产生链表,其参数是一个指向结构体的二级指针,因此在调用时

29、实在参数必须是一级指针的地址。主函数中初始调用时一级指针的内容为NULL,以后在每次递归调用时都要保证当时的一级指针的内容均为NULL,这在函数中是通过&(*h)-next)来实现的,因为在函数递归调用前已有p-next=NULL和*h=p这样的操作,这就使得(*h)-next的值为NULL。如图814所示。,图 8-14,8.2.3 堆栈 堆栈是一种受限制的链表,即添加和删除操作只能从一端进行的链表,其结构如图8-15所示。进行操作的这一端称为栈顶。向栈中添加对象只能加在当前的栈顶上,使它成为新的栈顶对象,这种操作称为“入栈”或“进栈”。,图815 堆栈的结构,1.堆栈的类型定义类型定义如下

30、:struct stackNode int data;struct stackNode*next;typedef struct stackNode snode;typedef snode*snodep;,(1)进栈函数push:void push(snodep*top,int info)snodep newp;newp=(snodep)malloc(sizeof(snode);if(newp!=NULL)newp-data=info;newp-next=*top;*top=newp;else printf(%d not inserted,No memory available.n,info);

31、,把新结点压入栈顶的操作步骤有下列三步:调用malloc函数,动态地建立一个新结点,把该结点的内存地址赋给newp,把要压入栈顶的数值info赋给newp-data(结点的数值域);把栈顶指针(*top)赋给newp-next(结点的指针域),从而使新结点的指针域指向原来的栈顶结点;把newp赋给*top,从而使*top指向新的栈顶结点。其操作示意图如图816所示。,图816 push的操作过程(a)push操作前的栈状态;(b)操作过程,(2)出栈函数pop:int pop(snodep*top)snodep temp;intvalue;temp=*top;value=(*top)-data

32、;top=(*top)-next;free(temp);return value;,图817 pop的操作过程(a)pop操作前的栈状态;(b)出栈过程,【例89】includeincludestruct stackNode int data;struct stackNode*next;typedef struct stackNode Snode;typedef Snode*Snodep;void push(Snodep*,int);int pop(Snodep*);,void prints(Snodep);void instruction(void);main()snodep stackp=

33、NULL;int i,v;instruction();printf(?);scanf(%d,while(i!=3)switch(i),case1:/*进栈操作*/printf(Enter an integern);scanf(%d,default:,printf(Invalid choicen);instruction();break;printf(?);scanf(%d,void instruction(void)printf(Enter choice:n1.to push a value into stack.n,2.to pop a value from stack.n 3.to end

34、 programn);void prints(Snodep p)if(p=NULL)printf(The stack is empty.nn);else printf(The stack is:n);while(p!=NULL)printf(%d-,p-data);p=p-next;,printf(NULLnn);运行输出:Enter your choice:1.To push a value into stack2.To pop a value from stack3.To end program?1Enter an integer5The stack is:5-NULL,?18The st

35、ack is:8-5-NULL?111The stack is:11-8-5-NULL?2The popped value is:11The stack is:8-5-NULL?4,Invalid choiceEnter your choice:1.to push a value into stack2.to pop a value from stack3.to end program?3End of run,8.2.4 队列 队列也是一种受限的链表,即对它增加新结点的操作只能在其尾部进行,从中删除结点的操作只能在头部进行,因而它是一种“先进先出(first in,first out,即FIF

36、O)”的数据结构。插入和删除的操作分别称为“入队(enqueue)”和“出队(dequeue)”操作。因此对队列的操作需要两个指针,一个指向队列头部(headp),另一个指向队列尾部(tailp)。图818的队列中箭头的指向就是从头部指向尾部。,图818 队列的结构,1.队列的类型定义类型定义如下:struct queuenode int data;struct queuenode*next;typedef struct queuenode Qnode;typedef Qnode*Qnodep;,2.入队和出队函数的定义 入队和出队操作都涉及对指针值的改变,因此函数中采用传引用调用方法,函数的

37、形参定义为二级指针,实参为指针的地址。(1)入队函数enqueue:函数形参是两个指针的指针以及要插入到队列中的值。入队操作主要有以下六个步骤:建立一个新结点,调用malloc函数开辟内存空间,把新结点的地址赋给newp;把要插入队列中的数值赋给结点的数值域newp-data;,对新结点的指针域赋以NULL值(这对插入的每个结点都要进行,因为新结点是插在队尾,而队尾的指针域必须是NULL);如果原队列为空,则新结点应为队列中的唯一结点,指向队列头的指针也必须指向它,即把newp赋给*headp;如果原队列不空,则不涉及队列头指针,只对队尾指针进行操作就可以了,即把newp赋给(*tailp)-

38、next,则新结点就加入队尾了。使队尾指针指向新结点,即把newp赋给*tailp。,图819 入队操作的过程,(2)出队函数dequeue:int dequeue(Qnodep*headp,Qnodep*tailp)int value;Qnodep temp;value=(*headp)-data;tamp=*headp;*headp=(*headp)-next;/*摘除队列头结点*/if(*headp=NULL)*tailp=Null;/*队列中无结点*/free(temp);return value;,图820 出队操作的过程,8.2.5 二叉树 前面讨论的数据结构都是线性数据结构,它们

39、的共同特点是除第一个结点和最后一个结点外,其他结点都有且只有一个前驱结点和后继结点。树是一种非线性的数据结构,如图821(a)所示。在这种数据结构中,除第一个结点(常称为树根)外,其他结点都有且只有一个前驱结点;所有的结点(包括根结点)都可以有0个或多个后继结点。在树结构中,前驱结点又称为“父结点”;后继结点又称为“子结点”;具有同一个父结点的结点称为“兄弟结点”;没有子结点的结点称为“叶结点”;自然,没有父结点的结点即为“根结点”。,图821 树的结构(a)普通树;(b)二叉树,图822 二叉树的分解,对二叉树的处理有三种次序:(1)先根次序:先处理根结点,再处理子结点;(2)中根次序:先处

40、理根的一棵子树,接着处理根结点,最后处理另一棵子树。(3)后根次序:先处理两棵子树,最后再处理根结点。,图823 二叉树的结构,图824 二叉查找树,在处理二叉查找树的程序中所用的主要函数是插入结点函数和遍历结点函数。在插入结点的函数中,因为要对指针进行修改,所以形参应定义成指针的指针,而实参是指针的地址。1.定义二叉查找树的数据类型 类型定义如下:struct treeNode struct treeNode*leftp;int data;struct treeNode*rightp;typedef struct treeNode treeN;,2.插入结点函数和遍历结点函数的定义(1)插入

41、结点函数insert如下:void insert(treeN*treep,int value)if(*treep=NULL)*treep=(treeN*)malloc(sizeof(treeN);if(*treep!=NULL)(*treep)-data=value;(*treep)-leftp=NULL;(*treep)-rightp=NULL;else,printf(%d not inserted,No memory availablen,value);elseif(valuedata)insert(/*标记重复*/,(2)遍历结点的函数因遍历的次序不同而有三个,这三个函数的构成语句都完全

42、一样,差别只在于语句的次序上。下面以中序遍历为例说明函数的构成:void inorder(treeN*treep)if(treep!=NULL)inorder(treep-leftp);/*遍历左子树*/printf(%3d,treep-data);/*输出根结点*/inorder(treep-rightp);/*遍历右子树*/,【例810】includeincludeincludeStruct treeNode struct treeNode*leftp;int data;struct treeNode*rightp;;typedef struct treeNode treeN;,void

43、insert(treeN*,int);void inorder(treeN*);void preorder(treeN*);void postorder(treeN*);main()int i,item;treeN*rootp=NULL;srand(time(NULL);printf(The number being placed in the tree are:n);for(i=1;i=10;i+)item=10+rand()%41;printf(%3d,item);,insert(void postorder(treeN*treep),if(treep!=NULL)postorder(tr

44、eep-leftp);postorder(treep-rightp);printf(%3d,treep-data);void preorder(treeN*treep)if(treep!=NULL)printf(%3d,treep-data);preorder(treep-leftp);,preorder(treep-right);void inorder(treeN*treep)if(treep!=NULL)inorder(treep-leftp);printf(%3d,treep-data);inorder(treep-rightp);,8.3 共用体,程序中的变量有两种情况:一种是变量之

45、间互不相关,都有自己的名字和存储空间;另一种是变量之间是相关的,它们虽然有各自的名字,但共用同一段内存空间,让这段空间轮流地为它们服务,这样就可以减少空间的浪费。让这些变量共用同一内存空间的方法是把它们组织成共用体。共用体是一种新的数据类型,它的定义与结构体的定义相似:union 成员列表;,例如:union numberint x;float y;char c;,图825 共用体对内存空间的占用,在这个共用体类型中定义了三个分量,它们的类型各不相同,但都占用同一内存空间,由于各个分量类型不同,所以这段空间应足够大,以便能放下最大的分量,所以这个共用体要占用4个字节空间,因为其中的分量y是fl

46、oat类型,是最长的类型,占4字节,如图825所示。,【例811】includemain()union number int i;long l;a;a.i=-32768;printf(int:%d,l ong:%ldn,a.i,a.l);a.l=65536;printf(int:%d,long:%ldn,a.i.,a.l);return 0;,运行输出:int:-32768,long:32768 int:0,long:65536 共用体类型和结构体类型可以互相嵌套,即可以互为对方分量的类型。一般的使用是在结构体中设一个类型标志分量,由该标志确定当前对共用体中哪个分量进行处理。,【例812】某单

47、位招聘博士人员,国内博士应注明取得博士学位的年份,国外博士要注明取得博士学位的国家。include main()struct doctor char name15;int age;int tag;union int date;char country15;catalogue;,person;printf(input name,age,tag:n);scanf(%s%d%d,person.name,printf(n%s,%5d,,person.name,person.age);if(person.tag=1)printf(%10dn,person.catalogue.date);if(perso

48、n.tag=2)printf(%sn,person.catalogue.country);return 0;,运行输出:Input name,age,tag:Zhang dong 35 1Input date-year:2000Zhang dong,35,2000再运行:Input name,age,tag:Zhaotian 32 2Input country name:Francezhaotian,32,France,注意:在嵌套的定义中,共用体类型名可以不写,但其分量名catalogue必须写,因对其分量进行引用时要用到这个分量;只有对最底层的分量才能进行输入/输出操作,如等,它们相当于两

49、个变量,其类型分别是int和char*的类型,因此输入/输出的控制字符应和它们相一致。标志tag取不同值时进行不同的操作,在程序中应予以提示说明,不然在输入时会造成困惑,甚至会输入不正确的数值。,8.4 位段,为了节约内存,C语言允许以位而不是以字节为单位来定义变量的大小。这样的变量一般用来定义结构体的成员。这些用存储位表示的变量称为“位段”。它们定义的一般形式是:unsigned|int:,花括号中的内容是必须的,而“|”两侧的内容任选其一。是标识符,指二进制位。如 unsigned a:4;则说明位段a占4位。位数决定了取值范围。4位二进制位的取值范围是0到15,超过15则a就容纳不下了。

50、若位段用int类型定义,则必须留出最高位为符号位,如 int b:4 则定义b的数值位只有3位,其所能表示的数的范围是-8到7。,对于只有1位的位段,必须定义成unsigned类型,如struct exunsigned a:12;unsigned b:3;unsigned c:1;int d:4;,对于不指定名字的位段表示不用它的空间,如struct examunsigned a:3;unsigne d:4;unsigned b:8;,【例813】设计一个打印扑克牌的程序。编程思路:牌的面值(face)共有13种(从Ace到King),可用4位二进制数表示(4位能表示0到15之间的值);牌在花

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号