第9章结构体、共用体与及枚举PPT课件.ppt
结构体、共用体与及枚举 9 2 l了解结构体、共用体和枚举类型的特点 l熟练掌握结构体类型、变量、数组、指针变量 的定义、初始化和成员的引用方法 l掌握共用体和枚举类型、变量的定义和引用 l掌握用户自定义类型的定义和使用 3 9.1 结构体 l【问题】如何表示下面的数据 王云平、18岁、男、学号2019010001、 汉族、北京、手机号13901000001 可以定义以下变量来分别表示上面的数据 char name10;int age;char sex3;char xh11; char nation20;char address20;char mobile20; 由于必须类型相同才能构造成数组,显然以前 学习的数据类型都不能很好地解决问题 4 9.1.1 结构体类型的定义 l结构体类型的定义形式为 struct 类型名 成员说明表列 ; l例如 struct student /* 结构体类型名 */ char name10; /*结构体成员,以下都是*/ int age; char sex3; char xh11; char nation20; char address20; char tel20; ; 5 9.1.1 结构体类型的定义 lstruct是结构体关键字,结构体类型定义中的每个成 员项都有确定的类型和名称,称为结构体类型的“域” ,每个域的定义后面要有“;”号。 l结构体类型由用户定义,所以结构体类型不是固定结 构的类型,用户可以定义不同结构的结构体类型,也 可以定义相同结构的结构体类型,系统均认为是不同 的结构体类型,例如下面是两个不同的结构体类型, 虽然aa和bb的结构是一样的 lstruct aaint a;int b;char c; lstruct bbint a;int b;char c; 6 9.1.2 结构体变量的定义和初始化 l定义结构体变量的方法可以如下 l用已定义的结构体类型名定义变量。例如 struct student wang,zhang; l在定义结构体类型的同时定义结构体变量。例如 struct student /* 结构体类型名 */ char name10; /*结构体成员,以下都是*/ int age; char sex3; char xh11; char nation20; char address20; char tel20; wang,zhang; 7 9.1.2 结构体变量的定义和初始化 l不定义结构体类型名,直接定义结构体变量。例如 struct char name10; int age; char sex3; char xh11; char nation20; char address20; char tel20; wang,zhang; 8 结构体类型的长度 l结构体类型的长度可以用sizeof运算符计算出来,形 式为 lsizeof结构体类型名 l或者 lsizeof变量名 l如sizeofstruct student 或sizeofwang,在TC和 VC下,结果分别是86和88。 9 结构体类型的嵌套 struct date int year; int month; int day; ; struct student char name10; int age; char sex3; char xh11; struct date birthday; char nation20; char address20; char tel20; wang,zhang; struct student char name10; int age; char sex2; char xh11; struct int year; int month; int day; birthday; char nation20; char address20; char tel20; wang,zhang; 10 结构体类型变量的初始化 l定义的同时初始化 lstruct student wang王云平,18,男,2019010001,汉 族,北京, 13901000001,zhang张丽,18,男 ,2019010002,汉族,广州, 13901000002; l注意初始化的数据及其类型要与各个成员一一对应,对于包 含嵌套结构体类型的变量,其嵌套部分的初始化也按顺序赋 初值 lstruct student wang王云平,18,男 ,2019010001,2019,3,3,汉族,北京,13901000001; 11 9.1.3 结构体变量的引用 l数组元素的引用采用数组名和下标结合的引用方法, 例如a2、b5等。结构体变量其成员的引用则采用 成员运算符“.”来完成,格式为 l结构体变量名.成员名 l或 l结构体变量名.结构体成员名..结构体成员名.基本成员名 l后者是指包含嵌套的结构体类型。 l例如前面定义的变量wang,其成员引用如下 lwang.age lwang.birthday.year 12 注意 l结构体的成员引用的形式比普通的变量(或数组)复杂一些,但 本质上相当于一个普通变量(或数组),可参与该成员所属数据 类型的一切运算。例如 lwang.age 20; liage 20; lprintfagedn,wang.age; lprintfagedn,iage wang.age; l l成员运算符“.”的优先级最高,在表达式中的结构体变量成员不 需要加括号。例如 lwang.age; l相当于 lwang.age; 13 注意 l结构体变量的成员名可以相同,但必须处在不同的层次。 例如 sturct student int no; char name20; struct int no; char classname20; class; struct int no; char groupname20; group; wang; l上面的结构体存在几个相同的成员no,但层次不同,其引 用形式能够区别开来,引用形式分别如下 lwang.no lwang.class.no lwang.group.no 14 注意 l同一类型的结构体变量可相互赋值。 l我们知道,数组之间不能整体赋值,但同类型的两 个结构体变量之间可以整体赋值,这样可以提高程 序的效率。例如 lzhang wang; lzhang.birthday wang.birthday; 15 【例9-1】演示结构体类型。 include include struct date int year;int month;int day; ; struct student char name10; int age; char sex3; char xh11; struct date birthday; char nation20; char address20; char tel20; ; 16 【例9-1】演示结构体类型。 main struct student wang Wang YunPing,18,M,2019010001, 2019,3,3,Han,Bei Jing, 13901000001,zhang; zhang wang; strcpyzhang.name,Zhang Li;strcpyzhang.xh,2019010002; zhang.birthday.year2019;zhang.birthday.month4; zhang.birthday.day4;strcpyzhang.address,Guang Zhou; strcpyzhang.tel,13901000001; printfs,d,s,s,,zhang.name,zhang.age,zhang.sex, zhang.xh; printfd,d,d,,zhang.birthday.year,zhang.birthday.month, zhang.birthday.day; printfs,s,sn,zhang.nation,zhang.address,zhang.tel; 17 9.1.4 结构体数组 l结构体类型既可以定义单个的变量,也可 以定义结构体数组,用以存储批量的数据 。 18 结构体数组的定义 l和结构体变量定义一样,结构体数组的定义也 有以下3种方法 l先定义结构体类型,用结构体类型名定义结构体数组, 例如 struct student; struct student stud50; l定义结构体类型名的同时定义结构体数组,例如 struct student stud50; l不定义结构体类型名,直接定义结构体数组,例如 struct stud50; 19 结构体数组的初始化 l和普通数组的元素是普通变量一样,结构体数 组的每一个元素相当于一个结构体变量,二者 的初始化也很类似,例如 lstruct student stud2 王云平,18,男,2019010001,汉族,北京 ,13901000001, 张丽,18,男,2019010003,汉族,广州 ,13901000003; 20 结构体数组的引用 l结构体数组元素的成员表示为 l结构体数组名下标.成员名 l或 l结构体数组名下标.结构体成员名..结构体成员名.成员名 l例如 lstudi.age lstud5.birthday.year l结构体数组元素和类型相同的结构体变量一样,可相互 赋值。例如 lstud1 stud0; l对于结构体数组元素内嵌的结构体类型成员,情况也相 同。例如 lstudent2.birthdaystudent1.birthday; 21 【例9-2】演示结构体数组的定义和应用。 include include struct date int year; int month; int day; ; struct student char name20; int age; char sex3; char xh11; struct date birthday; char nation20; char address20; char tel20; ; 22 【例9-2】演示结构体数组的定义和应用。 main struct student stud3 Wang YunPing,18,M,2019010001,2019,3,3, Han,Bei Jing,13901000001, Zhang Li,18,M,2019010002,2019,4,4,Han, Guang Zhou,13901000002, Gu YuPing,18,F,2019010003,2019,5,5,Han, Shang Hai,13901000003; int i; fori0;i3;i printfs,d,s,s,,studi.name,studi.age,studi.sex,studi.xh; printfd,d,d,,studi.birthday.year,studi.birthday.month, studi.birthday.day; printfs,s,sn,studi.nation,studi.address,studi.tel; 23 【例9-2】用键盘输入数据,程序修改为 struct student stud3; int i; fori0;iage l因为*p其实相当于wang,所以*p.age相当 于wang.age。 l“-”是一个运算符,和“.”优先级相同,具有最 高的优先级,用于成员的引用。 27 【例9-3】修改例9-2,利用结构 体指针变量访问数据。 struct student *p; int i; p printfs,d,s,s,,p-name,p-age,p-sex,p-xh; printfd,d,d,,p-birthday.year,p-birthday.month,p-birthday.day; printfs,s,sn,p-nation,p-address,p-tel; p fori0;iname,p-age,p-sex,p-xh; printfd,d,d,,p-birthday.year,p-birthday.month, p-birthday.day; printfs,s,sn,p-nation,p-address,p-tel; p; 28 【例9-3】修改例9-2,利用结构体指 针变量访问数据。 l第1行的输出是p指向结构体变量wang后输出的 。这样的访问方式和结构体变量访问方式差不 多。 l第2行至第4行是p指向结构体数组后输出的。当 p指向 int month; int day; ; struct student char name20; int age; char sex3; char xh11; struct date birthday; char nation20; char address20; char tel20; ; 31 【例9-4】打印学号为20190102学生 的年龄。 void showageint age printfAgedn, age; void show1struct student s /*结构体变量作为形参*/ printfs,d,s,s,,s.name,s.age,s.sex,s.xh; printfd,d,d,,s.birthday.year,s.birthday.month,s.birthday.day; printfs,s,sn,s.nation,s.address,s.tel; void show2struct student *p /*结构体指针作为形参*/ printfs,d,s,s,,p-name,p-age,p-sex,p-xh; printfd,d,d,,p-birthday.year,p-birthday.month,p- birthday.day; printfs,s,sn,p-nation,p-address,p-tel; void show3struct student s,int n /*结构体数组作为形参*/ int i; fori0;in;i printfs,d,s,s,,si.name,si.age,si.sex,si.xh; printfd,d,d,,si.birthday.year,si.birthday.month, si.birthday.day; printfs,s,sn,si.nation,si.address,si.tel; 32 【例9-4】打印学号为20190102学生 的年龄。 void main struct student wang Wang YunPing,18,M,2019010001,2019,3,3,Han,Bei Jing, 13901000001; struct student zhang Zhang Li,18,M,2019010002,2019,4,4,Han,Guang Zhou,13901000002; struct student stud3 Wang YunPing,18,M,2019010001,2019,3,3,Han,Bei Jing,13901000001, Zhang Li,18,M,2019010002,2019,4,4,Han,Guang Zhou,13901000002, Gu YuPing,18,F,2019010003,2019,5,5,Han,Shang Hai,13901000003; struct student *p;struct student t; printfDemo showagen;showagewang.age; /*结构体成员作为实参*/ printfDemo show1n;show1wang;/*结构体变量作为实参*/ pprintfDemo show2n; show2p; /*结构体指针作为实参,也可以写成show2 show3stud,3; /*结构体数组名作为实参*/ /*结构体变量交换*/ twang;wangzhang;zhangt; printfDemo swapn;show1zhang; 33 【例9-4】打印学号为20190102学生 的年龄。 l运行结果 34 【注意】 l由于结构体struct student作为主函数之外其他 函数的形式参数,所以结构体的定义需要放在 函数之外,不能放在主函数main内。 lshow1wang写成show1stud0效果一样, 结构体数组元素也相当于一个结构体变量,例 题中正好对应的成员数据也一样。 l结构体变量不同于数组体现在结构体变量名需 要计算才能得到结构体数据域的地址,如 l fori0;in;iifstrcmpsi.name,name 0break; return si; struct student* seek2struct student s,int n,char name int i; fori0;in;iifstrcmpsi.name,name 0break; return struct student* seek3struct student s,int n,char name int i;struct student *ps; fori0;iname,name 0break; elsep; return p; 36 【例9-5】演示函数返回结构体类型。 void show1struct student s printfs,d,s,s,,s.name,s.age,s.sex,s.xh; printfd,d,d,,s.birthday.year,s.birthday.month,s.birth day.day; printfs,s,sn,s.nation,s.address,s.tel; void show2struct student *p printfs,d,s,s,,p-name,p-age,p-sex,p-xh; printfd,d,d,,p-birthday.year,p-birthday.month,p- birthday.day; printfs,s,sn,p-nation,p-address,p-tel; 37 【例9-5】演示函数返回结构体类型。 main struct student stud3 Wang YunPing,18,M,2019010001,2019,3,3,Han,Bei Jing,13901000001, Zhang Li,18,M,2019010002,2019,4,4,Han,Guang Zhou,13901000002, Gu YuPing,18,F,2019010003,2019,5,5,Han,Shang Hai,13901000003; show1seek1stud,3,Gu YuPing; show2seek2stud,3,Zhang Li; show2seek3stud,3,Wang YunPing; 38 9.2 共用体 l为了节约内存或便于对数据进行处理,C语言 允许不同类型的数据共享在一段存储单元,这 种共享存储单元的特殊数据类型叫做“共用体” 类型,也可称之为“联合”类型。 l共用体的定义和结构体相似,可以借鉴结构体 部分,其中不同的地方在本节中将逐一指出。 39 9.2.1 共用体类型的定义 l共用体类型的定义形式为 union 类型名 成员说明列表 ; l例如 union data char c; float f; double d; ; 40 9.2.2 共用体变量的说明和引用 l与结构体变量的说明类似,也有3种方式 l先定义共用体类型,再用共用体类型定义共用体变量。 union 类型名 成员说明列表 ; union 类型名 共用体变量名表;。 例如,union data x; l定义共用体类型名的同时定义共用体变量。 union 类型名 成员说明列表共用体变量名表; l不定义类型名直接定义共用体变量 41 注意 lunion data或变量x,表达式sizeofunion data 和sizeofx的值均为8。 42 引用共用体变量 l引用共用体变量的形式以及注意事项均与引用结构体 变量相似,例如 lx.c /* 共用体字符型成员,相当于普通字符型变量 */ l对共用体变量中的任何一个成员赋值,都会导致共享 区域数据发生变化,所以共用体只能保证只有一个成 员的值是有效的。例如,对于共用体变量x,假设有 lx.f 3.14159; l必然使得地址10001011四个字节的内容发生变化 l整体引用共用体变量没有多大的意义,通常都是引用 共用体变量的成员 43 【例9-6】演示共用体类型的引用 include stdio.h include string.h struct date int year;int month;int day; ; union call char mobile20;int telephone;; struct student char name20; int age; char sex3; char xh11; struct date birthday; char nation20; char address20; union call callnumber; ; 44 【例9-6】演示共用体类型的引用 void main struct student wang Wang YunPing,18,M,2019010001,2019,3,3,Han,Bei Jing; struct student li Li Zhen,20,F,2019010001,2019,3,3,Han,He Fei; struct student *p; strcpywang.callnumberle,13901000001; li.callnumber.telephone 56023328; p printfs,d,s,s,,p-name,p-age,p-sex,p-xh; printfd,d,d,,p-birthday.year,p-birthday.month,p-birthday.day; printfs,s,sn,p-nation,p-address,p-callnumberle; p printfs,d,s,s,,p-name,p-age,p-sex,p-xh; printfd,d,d,,p-birthday.year,p-birthday.month,p-birthday.day; printfs,s,ldn,p-nation,p-address,p-callnumber.telephone; 45 【例9-7】运行下面的程序,分析 运行结果。 include include void main union keycode short int i; char c2; key; printfsizedn,sizeofkey; key.i 16961; /* 0 x4241 */ printfkey.c0 d,key.c1dn,key.c0, key.c1; strcpykey.c,AB; printfkey.c0 c,key.c1cn,key.c0, key.c1; printfkey.i d0 xxn,key.i,key.i; 46 【分析】 l共用体变量key有两个成员short int型成员i和字符 数组c,它们都占用2字节,因此共用体变量key长度 为2字节。 l给short int型成员i赋值16961,然后将该数据的两个 字节用字符数组c分别输出,低位字节key.c0为十六 进制数42,对应字符B;高位字节key.c1为十六进 制数41,对应字符A。 l给字符数组key.c赋值AB,得到的结果和前面一样 。由于长度的限制,这里strcpy没能把字符串结束符 存入key.c,对于本题没有关系。 l由此可以看出,key.i被key.c0、key.c1拆开成两 部分,从而分别取得其高位或低位字节部分的内容。 47 9.3 枚举类型 l引入 l假设有序列Sunday、Monday、Tuesday、 Wednesday、Thursday、Friday、Saturday, 从星期的名称上不能体现他们的顺序,但如果将其 与下面的序列对应就可以体现了0、1、2、3、4 、5、6。 l这两种序列都有优点,前者表达的意义自然明确, 容易接受;后者更能体现星期名称之间的顺序。 l能否将二者结合起来,形成一种新的数据类型 48 9.3.1 枚举类型的定义 l枚举类型定义的形式为 lenum 类型名标识符序列; l如 lenum weekSunday,Monday,Tuesday,Wednesd ay,Thursday,Friday,Saturday; lenum是定义枚举类型的关键字,枚举类 型week包含7个标识符序列,分别等于0 、1、2、3、4、5、6,这些标识符常量是 有序的。 49 注意 l枚举值标识符是常量不是变量,这些常量是基本数据类 型。 l枚举值只能是一些标识符,不能是基本类型常量。下面 的定义是错误的 lenum week0,1,2,3,4,5,6; l可以在定义枚举类型时对枚举常量重新定义值,如 lenum weekMonday1,Tuesday,Wednesday,Thursday,Friday, Saturday, Sunday; l这样对应的序列为1、2、3、4、5、6、7。 l下面的定义也是可以的 lenum colorblack,blue,green,red4,yellow14,white; l此时red为4,yellow为14,white为15。 50 9.3.2 枚举变量的定义和引用 l枚举变量的定义 l形式可以为 lenum类型名 变量名表; lenum类型名标识符序列 变量名表; lenum 标识符序列 变量名表; l例如 lenum color backcolor; lenum color black,blue,green,red4,yellow14,whitebackcolor; lenum black,blue,green,red4,yellow14,whitebackcolor; lenum week firstweek,nextweek; 51 枚举变量的引用 l正确的引用方式 lbackcolor red; lbackcolor 4; lbackcolor ; /*假设原来是red,现在将变成yellow了*/ lifbackcolor red printfThe color is red lscanfd, /*输入一个整型数给backcolor 变量,不过必须在枚举类型定义的范围之内,可以是0、1、 2、4、14、15,其他都是错误的。*/ l错误的引用方式 lbackcolor 3; /*不在枚举类型定义的范围之内*/ lbackcolor grey;/*不在枚举类型定义的范围之内*/ l由于枚举变量可以作为循环变量,因此可以利用循环和 switch语句打印全部的枚举值字符串。 52 【例9-8】输出全部的枚举值字符串。 include enum eweekMonday1,Tuesday,Wednesday,Thursday, Friday,Saturday, Sunday; void main char weekname720 Sunday,Monday,Tuesday,Wednesday,Thursday,F riday,Saturday; enum eweek week; forweek Monday ; week Sunday ; week printfdsn,week,weeknameweek7; 53 9.4 用户定义类型 lC语言不仅提供了丰富的数据类型,还允许用 于自己定义类型说明符,相当于允许用户为数 据类型取“别名”。所用的类型定义符是 typedef。 54 9.4 用户定义类型 l名称替换 l定义的形式为 ltypedef 类型名 别名; l“类型名”必须是系统提供的数据类型或用户已定义的数据类 型,“别名”是标识符。 l例如 ltypedefint INTEGER; ltypedefstruct studentSTUDENT; ltypedefstructint year;int month;int day DATE; ltypedefchar* CHAR; /*char* 是字符指针类型*/ 55 9.4 用户定义类型 l有了上面的替换,就可以定义相应类型的变量了 lINTEGER a,b;/*相当于int a,b */ lSTUDENT wang,zhang; /*相当于struct student wang,zhang; */ lDATE birthday; /* 相当于structint year;int month;int day birthday;*/ lCHAR stringHello World; /*相当于char * stringHello World */ lCHAR p /*相当于char * p l例如 ltypedef int NUM3; ltypedef char STRING20; l定义相应类型的变量 lNUM a,b; /*相当于 int a3,b3 */ lSTRING s; /*相当于char s20 */ l就定义了该结构体类型的变量和指针变量 57 9.4 用户定义类型 l注意 l定义新类型名时一般用大写的标识符,以便区别于习惯的写法, 并不是必须的。 l用typedef定义类型只是定义新的类型名而不是创建新的数据类型 。 l注意定义新类型名与宏替换的区别。例如 ltypedef int INTEGER; ldefine INTEGER int l上述定义的作用都是用标识符INTEGER代替int,但实质不同。 typedef是用标识符INTEGER代替类型“int”,而define是用标识 符INTEGER代替字符串“int”;typedef在编译时解释INTEGER, 而define是在编译之前将INTEGER替换成字符串“int”;typedef 并不是做简单替换,例如 ltypedef int NUM3; l不是简单地将NUM3替换成int,因为NUM a;相当于int a3;而不 是int a;。 58 9.5 程序举例 l【例9-9】编程求两个复数的和。 include struct complex double r; double i;; struct complex addstruct complex x,struct complex y struct complex z; z.rx.ry.r;z.ix.iy.i; return z; void main struct complex z,addstruct complex,struct complex; struct complex x1.2,2.5,y2.4,5.6; zaddx,y; printfxy.2f.2fin,z.r,z.i; 59 【例9-10】已知今天的日期,编 程求出明天的日期。 include struct date int year,month,day; ; int judgestruct date *pd int l_year0; if pd-year40 return l_year; int day_nostruct date *pd int day;int month130,31,28,31,30,31,30,31,31,30,31,30,31; if judgepd elsedaymonthpd-month; return day; 60 【例9-10】已知今天的日期,编 程求出明天的日期。 main struct date today,tomorrow; int judgestruct date*,day_nostruct date *; printfEnter todayyyyy,mm,dd ; scanfd-d-d, if today.dayday_no tomorrow.monthtoday.month;tomorrow.yeartoday.year; else if today.month12 tomorrow.day1;tomorrow.month1;tomorrow.yeartoday.year1; else tomorrow.day1;tomorrow.monthtoday.month1; tomorrow.yeartoday.year; printfTomorrows date is d-d-dn, tomorrow.year,tomorrow.month,tomorrow.day; 61 l本章介绍了指针的概念以及指针变量的定义和 初始化等。C语言的指针变量形式有 l一级指针变量int *p,p可指向变量、数组元素。 l二级指针变量int **pp,pp可指向一级指针变量 。 l指向一维数组的指针变量int *pn,可用于二 维数组的行指针变量。 l指针数组int *pn,元素是一级指针变量。 l指向函数的指针变量int *p,p可指向一个函 数。 l返回指针的函数int *f,f函数返回一个一级 指针。 62 l结构体与共用体相似的地方 l类型定义的形式相同。 l变量说明的方法相同。都有3种方法说明变量。 l结构体与共用体的引用方式相同。除了同类型的变量之间可 赋值外,均不能对变量整体赋常数值、输入、输出和运算等 ,都只能通过引用其成员项进行,嵌套结构只能引用其基本 成员 l结构体或共用体的(基本)成员是