结构体和共用体.ppt
第8章 结构体和共用体,前面的章节中已经介绍了各种基本数据类型、数组和指针。但只有这些数据类型还难以处理一些比较复杂的数据结构。本章将以前面介绍的数据类型为基础,进一步介绍结构体类型、共用体类型和枚举类型。,8.1 结构体 8.2 动态内存分配与链表 8.3 共用体类型 8.4 枚举类型 8.5 用户自定义类型 8.6 程序举例,第8章 结构体和共用体,第8章,8.1 结 构 体,8.1 结构体,8.1.1 结构类型定义 在实际问题中,一组数据往往具有不同的数据类型。例如,在学生登记表中,姓名应为字符型;学号可为整型或字符型;年龄应为整型;性别应为字符型;成绩可为整型或实型。但这些显然不能用一个数组来存放这一组数据。因为数组中各元素的类型和长度都必须一致,以便于编译系统处理。为了解决这个问题,语言中给出了另一种构造数据类型“结构体”。“结构体”是一种构造类型,它是由若干“成员”组成的。每一个成员可以是一个基本数据类型或者又是一个构造类型。结构体既然是一种“构造”而成的数据类型,那么在说明和使用之前必须先定义它,也就是构造它。如同在说明和调用函数之前要先定义函数一样。,8.1 结构体,定义一个结构体类型的一般形式为:struct 结构体名 结构成员的说明;成员表由若干个成员组成,每个成员都是该结构体的一个组成部分。对每个成员也必须作类型说明,其形式为:类型说明符 成员名;成员名的命名应符合标识符的书写规定。例如:struct stu int num;char name20;char sex;float score;,8.1 结构体,在这个结构体定义中,结构体名为stu,该结构体由4个成员组成。第一个成员为num,整型变量;第二个成员为name,字符数组变量;第三个成员为sex,字符变量;第四个成员为score,实型变量。应注意在括号“”后的分号是不可少的。结构体定义之后,即可进行变量说明。凡说明为结构体stu的变量都由上述4个成员组成。由此可见,结构是一种复杂的数据类型,是数目固定,类型不同的若干有序变量的集合。,8.1 结构体,8.1.2 结构体类型变量的说明 说明结构体变量有以下三种方法。以上面定义的stu为例来加以说明。(1)先定义结构体类型,再说明结构体变量 例如:struct stu int num;char name20;char sex;float score;struct stu boy1,boy2;说明了两个变量boy1和boy2为stu结构类型。也可以用宏定义使用一个符号常量来表示一个结构类型,例如:#define STU struct stu STU int num;char name20;char sex;float score;STU boy1,boy2;,8.1 结构体,(2)在定义结构体类型的同时说明结构体变量 例如:struct stu int num;char name20;char sex;float score;boy1,boy2;(3)直接说明结构体变量 例如:struct int num;char name20;char sex;float score;boy1,boy2;,8.1 结构体,第三种方法与第二种方法的区别在于第三种方法中省去了结构体名,而直接给出结构体变量。三种方法中说明的boy1,boy2变量都具有相同的结构。说明了boy1,boy2变量为stu类型后,即可向这两个变量中的各个成员赋值。在上述stu结构体定义中,所有的成员都是基本数据类型或数组类型。成员也可以又是一个结构体类型,即构成了嵌套的结构体。,8.1 结构体,例如:struct date int month;int day;int year;struct int num;char name20;char sex;struct date birthday;float score;boy1,boy2;,首先定义一个结构体date,由month(月)、day(日)、year(年)三个成员组成。在定义并说明变量 boy1 和 boy2 时,其中的成员birthday被说明为data结构体类型。成员名可与程序中其它变量同名,互不干扰。结构体变量成员的表示方法,在程序中使用结构体变量时,往往不把它作为一个整体来使用。,说明:结构体在内存中存储容量是各成员容量之和,这是与后面联合体的重要区别。,8.1 结构体,8.1.3 结构体变量的引用 一般情况下,不能对一个结构体变量作为整体引用,只能引用其中的成员。结构体变量中成员引用的一般形式为:结构体变量名.成员名其中,“.”是域成员运算符,是C语言中优先级最高的运算符之一。例如:boy1.num 即第一个人的学号,boy2.sex 即第二个人的性别。如果成员本身又是一个结构体,则必须逐级找到最低级的成员才能使用。例如:boy1.birthday.month 即第一个人出生的月份。成员可以在程序中单独使用,与普通变量完全相同。,8.1 结构体,8.1.4 结构体变量的赋值对于结构体变量,只有以下两种情况可以对结构体变量赋值。(1)结构体变量整体赋值 例如:boy2=boy1;(2)取结构体变量地址 例如:这些语句都是不允许的,只能对结构体成员进行输入/输出。,8.1 结构体,例8.1 给结构体变量赋值并输出其值。#include void main()struct stu/*定义结构体stu*/int num;char*name;char sex;float score;boy1,boy2;/*定义stu类型的变量boy1、boy2*/boy1.num=102;boy1.name=Zhang ping;printf(input sex and score:n);scanf(%c%f,8.1 结构体,程序运行结果:input sex and score:M 96number=102name=Zhang pingsex=Mscore=96.00,本程序中用赋值语句给num和name两个成员赋值,name是一个字符串指针变量。用scanf()函数动态地输入sex和score成员值,然后把boy1的所有成员的值整体赋予boy2。最后分别输出boy2 的各个成员值。,8.1 结构体,8.1.5 结构体变量的初始化 如果结构体变量为全局变量或者静态变量,则可以对它做初始化赋值。对局部或自动结构体变量不能做初始化赋值。,8.1 结构体,例8.2 外部结构体变量初始化。#include struct stu/*定义结构体*/int num;char*name;char sex;float score;boy2,boy1=102,Zhang ping,M,78.5;/*对变量boy1的成员初始化*/void main()boy2=boy1;/*把boy1整体赋给boy2*/printf(number=%dnname=%sn,boy2.num,boy2.name);printf(sex=%cnscore=%6.2fn,boy2.sex,boy2.score);,8.1 结构体,程序运行结果:number=102name=Zhang pingsex=Mscore=78.50,本程序中,boy2,boy1均被定义为外部结构体变量,并对boy1作了初始化赋值。在main()函数中,把boy1的值整体赋予boy2,然后用两个printf()语句输出boy2各成员的值。,8.1 结构体,例8.3 静态结构体变量初始化。#include void main()static struct stu/*定义静态结构体*/int num;char*name;char sex;float score;boy2,boy1=102,Zhang ping,M,78.5;/*对变量boy1的成员初始化*/boy2=boy1;printf(number=%dnname=%sn,boy2.num,boy2.name);printf(sex=%cnscore=%6.2fn,boy2.sex,boy2.score);本程序是把boy1,boy2都定义为静态局部的结构体变量,同样可以做初始化赋值。,8.1 结构体,8.1.6 结构体数组 一个结构体变量可以处理一个对象,如果有多个对象,则需要多个结构体变量,数组的元素也可以是结构体类型的,因此可以构成结构体数组。结构体数组的每一个元素都是具有相同结构体类型的下标结构体变量。在实际应用中,经常用结构体数组来表示具有相同数据结构的一个群体。如一个班的学生档案,一个车间职工的工资表等。结构体数组的定义方法和结构体变量相似,也有三种方式:(1)先定义结构体类型,再定义结构体数组。例如:struct stu int num;char*name;char sex;float score;;struct stu boy5;定义了一个结构体数组boy,共有5个元素,boy0boy4。每个数组元素都具有struct stu的结构体形式。,8.1 结构体,(2)在定义结构体类型的同时定义结构体数组。例如:struct stu int num;char*name;char sex;float score;boy5;(3)直接定义结构体数组。例如:struct int num;char*name;char sex;float score;boy5;,8.1 结构体,对外部结构体数组或静态结构体数组可以做初始化赋值。例如:struct stu int num;char*name;char sex;float score;boy5=101,Li ping,M,45,102,Zhang ping,M,62.5,103,He fang,F,92.5,104,Cheng ling,F,87,105,Wang ming,M,58;当对全部元素做初始化赋值时,也可不给出数组长度。,8.1 结构体,例8.4 计算学生的平均成绩和不及格的人数。#include struct stu/*定义结构体*/int num;char*name;char sex;float score;boy5=101,Li ping,M,45,102,Zhang ping,M,62.5,103,He fang,F,92.5,104,Cheng ling,F,87,105,Wang ming,M,58;/*对结构体数组元素初始化*/void main()int i,c=0;float ave,s=0;for(i=0;i5;i+)s+=boyi.score;if(boyi.score60)c+=1;printf(s=%6.2fn,s);ave=s/5;/*计算平均成绩*/printf(average=%6.2fncount=%dn,ave,c);,8.1 结构体,程序运行结果:s=345.00average=69.00count=2,本例程序中定义了一个外部结构体数组boy,共5个元素,并作了初始化赋值。在main函数中用for语句逐个累加各元素的score 成员值存于s之中,如score的值小于60(不及格),即计数器C加1,循环完毕后计算平均成绩,并输出全班总分、平均分及不及格人数。,8.1 结构体,例8.5 建立同学通讯录。#include#define NUM 2struct mem/*定义结构体*/char name20;char phone10;void main()struct mem manNUM;int i;for(i=0;iNUM;i+)/*输入通讯录*/printf(input name:);gets(mani.name);printf(input phone:);gets(mani.phone);printf(NamettPhonen);for(i=0;iNUM;i+)/*输出通讯录*/printf(%st%sn,mani.name,mani.phone);,8.1 结构体,程序运行结果:input name:Zhang juninput phone:88888888input name:Wang fanginput phone:99999999Name PhoneZhang jun 88888888Wang fang 99999999,本程序中定义了一个结构体类型mem,它有两个成员name和phone,用来表示姓名和电话号码。在主函数中定义man为具有mem 类型的结构体数组。在for语句中,用gets()函数分别输入各个元素中两个成员的值。然后又在for语句中用printf()语句输出各元素中两个成员值。,8.1 结构体,8.1.7 指向结构体变量的指针变量 结构体指针变量是一个指针变量,用来指向改变量所分配的存储区域的首地址。结构体指针变量还可以用来指向结构体数组中的元素。结构体指针与以前介绍的各种指针在特性和使用方法上完全相同。结构体指针变量的运算也按照C语言的地址计算规则进行的。例如,结构体指针变量加1将指向内存中下一个结构体变量,结构体指针变量自身地址值的增加量取决于它所指向的结构体变量的数据长度(sizeof()函数获取)。总之,结构体指针变量是指向一个结构体变量的指针变量。,8.1 结构体,1.结构体指针变量的定义 定义结构体指针变量的一般形式为:struct 结构体类型名*结构指针变量名;例如:struct stu int num;char*name;char sex;float score;boy,*pstu;pstu=也可以定义结构体类型后再定义结构体指针变量。结构体名和结构体变量是两个不同的概念,不能混淆。结构体名只能表示一个结构体形式,编译系统并不对它分配内存空间。只有当某变量被说明为这种类型的结构时,才对该变量分配存储空间。有了结构指针变量,就能更方便地访问结构变量的各个成员。,8.1 结构体,2.结构体指针变量的赋值 结构体指针变量必须先赋值后使用。赋值是把结构体变量的首地址赋给该指针变量,不能把结构名赋给该指针变量。例如,不能写成pstu=。,8.1 结构体,3.结构体指针变量的引用 定义好一个结构体指针变量之后,就可以对该指针变量进行各种操作。例如,给一个结构体变量指针赋一个地址值,输出一个结构体变量指针的成员值,访问结构体变量指针所指向的变量的成员等。引用结构体指针变量的一般形式为:(*结构指针变量).成员名;或 结构指针变量-成员名;例如:(*pstu).num;或 pstu-num;应该注意(*pstu)两侧的括号不可少,因为成员符“.”的优先级高于“*”。如去掉括号写作*pstu.num,则等效于*(pstu.num),这样,意义就完全不对了。,8.1 结构体,例8.6 分析下面程序的运行结果。#include struct stu/*定义结构体*/int num;char*name;char sex;float score;boy1=102,Zhang ping,M,78.5,*pstu;void main()pstu=,8.1 结构体,本程序序定义了一个结构体类型stu,定义了stu类型结构变量boy1 并作了初始化赋值,还定义了一个指向stu类型结构体的指针变量pstu。在main()函数中,pstu被赋予boy1的地址,因此pstu指向boy1。然后在printf()语句内用三种形式输出boy1的各个成员值。,程序运行结果:number=102name=Zhang pingsex=Mscore=78.50,8.2 动态内存分配与链表,我们存储数量比较多的同类型或同结构的数据时,一般首先考虑数组。然而在实际应用中,当处理一些难以确定其数量的数据时,如果用数组来处理,必须事先分配一个足够大的连续空间,以保证数组元素数量充分够用,但这样处理时对存储空间的一种浪费。C 语言使用动态内存分配来解决这样的问题,其中常用的就是链表。链表是一种常见的数据结构,它动态地进行存储分配,并且可以方便而又简单地进行数据插入,删除等操作。,8.2 动态内存分配与链表,8.2.1 链表的概念 链表是指若干个数据按一定的原则连接起来。这个原则为:前一个数据指向下一个数据,只有通过前一个数据项才能找到下一个数据项。链表有一个“头指针”(head),它指向链表的第一个元素(数据项)。链表的一个元素称为一个“结点”(node)。结点中包含两部分内容,第一部分是结点数据本身,如图8-1中的、所示。结点的第二部分是一个指针,它指向下一个结点。最后一个结点称为“表尾”,表尾结点的指针不指向任何地址,因此为空(NULL)。,图8-1 链表结构图,8.2 动态内存分配与链表,如果每个结点采用一个指针,将前一个结点的指针指向下一个结点,这称为单链表。如果每个结点有两个指向其他结点的指针,则称为双链表。本节主要讨论单链表的运算。由以上简单链表可以看到,链表中的每个结点至少包含两个域,一个域用来存放数据,其类型根据需存放的数据类型定义。另一个域用来存放下一个结点的地址,因此必然是一个指针类型,此指针的类型应该是所指向的表结点的结构体类型。在C语言中,可以用结构体类型来实现链表,例如:struct student int long;float score;struct student*next;/*指向下一结点*/;其中next是结构体指针变量,用来存放下一个结点的地址,即next是指向下一个结点。,8.2 动态内存分配与链表,8.2.2 动态存储分配 C语言允许在函数执行部分的任何地方使用动态存储分配函数开辟或收回存储单元,这样的存储分配叫动态存储分配。动态分配使用自由、节约内存。链表是动态分配存储空间的,也就是说在需要的时候才开辟一个结点的存储空间。在C语言中提供了以下有关的函数来实现动态存储分配和释放,这些函数包含在“stdio.h”或“malloc.h”中。,8.2 动态内存分配与链表,1.malloc()函数(分配内存空间函数)调用形式为:void*malloc(size);其作用是在内存中动态获取一个大小为size个字节的连续存储空间。该函数将返回一个void类型的指针,若分配成功,就返回所分配的空间的起始地址,否则,就返回空指针(NULL)。2calloc函数(分配内存空间函数)调用形式为:void*calloc(unsigned n,unsigned size);其作用是在内存中动态获取n个大小为size个字节的存储空间。该函数将返回一个void类型的指针,若分配成功,就返回内存单元的起始地址,否则,返回空指针(NULL)。用该函数可以动态地获取一个一维数组空间,其中n为数组元素个数,每个数组元素的大小为size个字节。,8.2 动态内存分配与链表,3free()函数(释放内存空间函数)调用形式为:void free(void*p);其作用是释放由p指针所指向的内存空间。即系统回收,使这段空间又可以被其他变量所用。指针变量p是最近一次调用malloc()或calloc()函数时返回的值,不能是任意的地址。4.realloc函数 调用形式为:void*recalloc(void*p,unsigned size);其作用是将p所指的已分配的内存空间重新分配成大小为size个字节的空间。它用于改变已分配的空间的大小,可以增减单元数。函数返回新内存的首地址,如果内存不够,则返回空指针(NULL)。,8.2 动态内存分配与链表,例8.7 分配一块区域,输入一个学生数据。#include#include void main()struct stu/*定义结构体*/int num;char*name;char sex;float score;*ps;/*定义一个结构体指针变量ps*/ps=(struct stu*)malloc(sizeof(struct stu);ps-num=102;/*输入学生数据*/ps-name=Zhang ping;ps-sex=M;ps-score=62.5;printf(number=%dnname=%sn,ps-num,ps-name);printf(sex=%cnscore=%6.2fn,ps-sex,ps-score);free(ps);,8.2 动态内存分配与链表,程序运行结果:number=102name=Zhang pingsex=Mscore=62.50,本程序中,定义了结构体类型stu,定义了stu类型指针变量ps。然后分配一块stu大内存区,并把首地址赋予ps,使ps指向该区域。再以ps为指向结构体的指针变量对各成员赋值,并用printf()输出各成员值。最后用free()函数释放ps指向的内存空间。整个程序包含了申请内存空间、使用内存空间、释放内存空间三个步骤,实现存储空间的动态分配。,8.2 动态内存分配与链表,8.2.3 建立和输出链表 所谓动态建立链表是指在程序执行过程中从无到有地建立链表,将一个个新生成的结点顺次链接入已建立的链表上,上一个结点的指针域存放下一个结点的起始地址,并给各结点数据域赋值。所谓输出链表是将链表上各个结点的数据域中的值依次输出,直到链表结尾。,8.2 动态内存分配与链表,例8.8 以三个结构体变量为结点建立一个简单的链表并输出。#include struct node int data;struct node*next;void main()struct node a,b,c,*head,*p;head=,程序运行结果:5-10-15-NULL,8.2 动态内存分配与链表,8.2.4 链表的基本操作 链表的基本操作包括,建立并初始化链表,遍历访问链表(包括查找结点、输出结点等),删除链表中的结点,在链表中插入结点。链表的各种基本操作的步骤如下。1.建立链表 建立头结点(或定义头指针变量)。读取数据。生成新结点。将数据存入结点的数据域中。将新结点连接到链表中(将新结点地址赋给上一个结 点的指针域连接到链表)。重复步骤,直到尾结点为止。,8.2 动态内存分配与链表,2遍历访问链表 输出链表即顺序访问链表中各结点的数据域,方法是:从头结点开始,不断地读取数据和下移指针变量,直到尾结点为止。3删除链表中的一个结点 找到要删除结点的前驱结点。将要删除结点的后驱结点的地址赋给要删除结点的前驱 结点的指针域。将要删除结点的存储空间释放。4在链表的某结点前插入一个结点 开辟一个新结点并将数据存入该结点的数据域。找到插入点结点。将新结点插入到链表中,将新结点的地址赋给插入点上 一个结点的指针域,并将插入点的地址存入新结点的指 针域。,8.2 动态内存分配与链表,例8.9 建立并输出一个学生成绩链表(假设学生成绩表中只含姓名和成绩)。#include#include typedef struct student/*自定义链表结点数据类型名ST和指针类型名*STU*/char name20;int score;struct student*next;/*结点指针域*/ST,*STU;STU createlink(int n)/*建立一个由n个结点构成的单链表函数,返回结点指针类型*/int i;STU p,q,head;if(nname,8.2 动态内存分配与链表,for(i=1;iname,/*调用输出链表的函数*/,8.2 动态内存分配与链表,程序运行结果:input number of node:4input datas:A 60B 70C 80D 90A 60B 70C 80D 90,8.2 动态内存分配与链表,例8.10编写一个函数,在例8.9中建立的链表的前面插入一个结点。#include#include/*将例8.9中typedef struct student直到void list(STU head)函数全部插入到该位置*/STU increasenode1(STU head)STU s;s=(STU)malloc(sizeof(ST);printf(Input new node datas:);scanf(%s%d,s-name,8.2 动态内存分配与链表,程序运行结果:input number of node:3input datas:A 60B 70C 80A 60B 70C 80input new node datas:E 100E 100A 10B 20C 30,8.2 动态内存分配与链表,例8.11 编写一个函数,在例8.9建立的链表的第i个结点之后插入一个新结点。#include#include/*将例8.9中typedef struct student直到void list(STU head)函数全部插入到该位置*/STU increasenode2(STU head,int i)STU s,p,q;int j=0;/*查找第i个结点计数用*/if(iname,/*查找新结点的位置,在p和q之间*/,8.2 动态内存分配与链表,while(jnext;if(jnext=s;/*在p和q之间,即第i个结点之后插入新结点*/s-next=q;return(head);void main()STU h;int n;int i;printf(input number of node:);scanf(%d,8.2 动态内存分配与链表,程序运行结果:input number of node:3input datas:A 60B 70C 80A 60B 70C 80input new node number:2input new node datas:E 100A 10B 20E 100C 30,8.2 动态内存分配与链表,例8.12 删除链表中的表首结点函数。#include#include/*将例8.9中typedef struct student直到void list(STU head)函数全部插入到该位置*/STU deletenode1(STU head)STU s;if(head!=NULL)printf(after deleted the first node:n);s=head;head=s-next;free(s);return(head);void main()STU h;int n;printf(input number of node:);scanf(%d,8.2 动态内存分配与链表,程序运行结果:input number of node:4input datas:A 60B 70C 80D 90A 60B 70C 80D 90after deleted the first node:B 70C 80D 90,8.2 动态内存分配与链表,例8.13 编写一个函数,删除链表中的第i个结点。#include#include/*将例8.9中typedef struct student直到void list(STU head)函数全部插入到该位置*/STU deletenode2(STU head,int i)STU p,s;int j;if(inext;free(s);return(head);s=head-next;/*查找第i个结点的位置,以s标记*/p=head;j=2;,8.2 动态内存分配与链表,while(jnext;if(jnext=s-next;/*摘除s结点*/free(s);/*回收已摘掉的结点*/return(head);void main()STU h;int n;int i;printf(input number of node:);scanf(%d,8.2 动态内存分配与链表,程序运行结果:input number of node:4input datas:A 60B 70C 80D 90A 60B 70C 80D 90which node you want to delete:2A 60C 80D 90,8.3 共用体类型,8.3 共用体类型,8.3.1 共用体类型概述 在实际问题中有很多这样的例子。例如在学校的教师和学生中填写以下表格:姓名、年龄、职业、单位。“职业”一项可分为“教师”和“学生”两类。对“单位”一项学生应填入班级编号,教师应填入某系某教研室。班级可用整型量表示,教研室只能用字符类型。要求把这两种类型不同的数据都填入“单位”这个变量中,就必须把“单位”定义为包含整型和字符型数组这两种类型的“联合”。这种几种不同类型的变量占用同一段内存空间的结构称为共用体(又叫联合)。共用体与结构体有一些相似之处,但两者有本质上的不同。在结构体中各成员有各自的内存空间,一个结构体变量的总长度是各成员长度之和。而在共用体中,各成员共享同一段内存空间,一个共用体变量的长度等于各成员中最长的长度。,8.3 共用体类型,定义一个共用体类型的格式为:union 共用体类型名 类型标识符1 成员1;类型标识符1 成员2;类型标识符1 成员n;其中union是系统指定的关键字,共用体类型名由用户指定,但要符合标识符的规定。它与结构体类型的根本区别是成员表的所有成员在内存中从同一地址开始存放。例如:union data int i;char c;float a;,8.3 共用体类型,定义了一个名为data的共用体类型,它含有3个成员,一个为整型,成员名为i;一个为字符型,成员名为c;一个为实型,成员名为a。这3个成员的内存空间虽然不同,但都从同一起始地址开始存储,如图8-2所示(图中一个框代表一个字节)。,图8-2 共用体成员的存储,对于具有上述相同成员的结构体变量,则系统分配的内存空间等于各成员的内存长度之和,即为13字节;而对于共用体变量,则该变量所占内存空间为所有成员中最长的成员的长度,即为8字节。,8.3 共用体类型,定义了共用体类型之后,可用它来说明共用体变量。共用体变量的定义方式和结构体变量的定义方式相同,也有三种形式。(1)先定义共用体类型,再用共用体类型定义该类型的共用体变量 union perdata int class;char officae10;union perdata a,b;则先定义共用体类型perdata,再定义该类型的共用体变量a、b。(2)在定义共用体类型的同时,定义该类型的共用体变量 union perdata int class;char office10;a,b;则定义共用体类型perdata的同时,定义了该类型的共用体变量a、b(3)不定义共用体名直接定义共用体变量 union int class;char office10;a,b;则直接定义了共用体变量a、b。,8.3.2 共用体变量的定义,8.3 共用体类型,共用体变量的引用和结构体变量的引用一样,不能对一个共用体变量作为整体来引用,只能引用其中的成员。共用体变量中成员引用的一般形式为:共用体变量名.成员名;如a被定义为上述perdata类型的变量之后,可使用a.class、a.office。另外,也可以通过指针变量引用共用体变量的成员。例如:union perdata*p,a;p=,8.3.3 共用体变量的引用和赋值,8.3 共用体类型,对于共用体变量的赋值,不允许只用共用体变量名作赋值或其他操作,也不允许对共用体变量作初始化赋值,赋值只能在程序中进行。还要再强调说明的是,一个共用体变量,每次只能赋予一个成员值。换句话说,一个共用体变量的值就是共用体变量的某一个成员值。,8.3 共用体类型,例8.14 设有一个教师与学生通用的表格,教师数据有姓名、年龄、职业、教研室四项。学生有姓名、年龄、职业、班级四项。编写程序输入人员数据,再以表格形式输出。#include#include#include void main()struct char name10;int age;char job;union/*定义共用体变量*/int class1;char office10;depa;body2;/*定义结构体数组*/,8.3 共用体类型,int n,i;for(i=0;i2;i+)printf(input name,age,job and department:);scanf(%s%d%c,bodyi.name,8.3 共用体类型,程序运行结果:input name,age,job and department:Chen 18 S 2input name,age,job and department:Zhang 51 T profName age job class/officeChen 18 S 2Zhang 51 T prof,本例程序用一个结构数组body来存放人员数据,该结构共有四个成员。其中成员项depa是一个共用体类型,这个共用体又由两个成员组成,一个为整型量class,一个为字符数组office。在程序的第一个for语句中,输入人员的各项数据,先输入结构的前三个成员name,age和job,然后判别job成员项,如为“s”则对共用体depa.class输入(对学生赋班级编号),否则对depa.office输入(对教师赋教研室名)。,8.4 枚举类型,8.4 枚举类型,8.4.1 枚举类型的概念及定义,若一个变量只有几种可能的值(例如,一个星期内只有7天,一年只有十二个月等),则可使用C语言中的枚举类型数据。“枚举”就是将变量可能的值一一列举出来,而变量的值只能取其中之一。枚举类型定义的格式为:enum 枚举类型名枚举元素名1,枚举元素名2,枚举元素名n;例如:enum weekdaysun,mon,tue,wed,thu,fri,sat;该枚举名为weekday,枚举值共有7个,即一周中的七天。,8.4 枚举类型,如同共用体和结构体一样,定义枚举类型变量的定义也有以下三种形式。(1)先定义枚举类型,再定义该类型的枚举变量 enum weekday sun,mon,tue,wed,thu,fri,sat;enum weekday a,b;则先定义枚举类型weekday,再定义该类型的枚举变量a、b。(2)在定义枚举类型的同时,定义该类型的枚举变量 enum weekday sun,mon,tue,wed,thu,fri,sat a,b;则在定义枚举类型weekday的同时,定义了该类型的枚举变量a、b。(3)直接定义枚举变量 enum sun,mon,tue,wed,thu,fri,sat a,b;则直接定义了枚举变量a、b。,8.4 枚举类型,8.4.2 枚举类型变量的赋值和使用,(1)枚举类型定义中枚举元素都用标识符表示,但都是常量,而不是变量。因此不能为枚举元素赋值。例如,对枚举类型weekday的元素不能作以下赋值:sun=5;mon=2;sun=mon;(2)每个枚举元素都有一确定的值,C语言编译系统按定义时枚举元素出现的次序使它们的值,从0开始顺序定义为0,1,2,。如在weekday中,sun值为0,mon值为1,sat值为6(这种方法被称为隐式方法)。也可以在定义枚举类型时,显示地给出枚举元素的值。例如:enum weekday sun=7,mon=1,tue,wed,thu,fri,sat;定义了sun的值为7,mon的值为1,以后顺序加1,即tue为2,sat为6。,8.4 枚举类型,(3)只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。例如:a=sum;/*正确*/a=0;/*错误*/如一定要把数值赋予枚举变量,则必须用强制类型转换,如a=(enum weekday)2;其意义是将顺序号为2的枚举元素赋予枚举变量a,相当于a=tue;还应该说明的是枚举元素不是字符常量也不是字符串常量,使用时不要加单引号或双引号。(4)在VC+6.0环境下,不能进行枚举元素加减一个整数的运算(也不能进行枚举元素的+、-运算),但可以通过类型转换实现。例如:a=sun+3;/*试图得到枚举值wed,但不正确*/a=(enum weekday)(sun+3);/*正确,得到枚举值wed*/,8.4 枚举类型,(5)枚举值可以比较大小。例如:if(workday=sun)printf(sun);if(work