结构体、共用体和用户定义类型.ppt
第13章 复杂数据类型,本章要点理解结构体的含义、定义与使用方法理解共用体的含义、定义与使用方法理解枚举类型的含义、定义与使用方法理解typedef的作用与使用方法掌握链表操作的基本方法本章难点有关链表的操作用结构体变量和指向结构体的指针作函数参数,概述,有时,需将不同类型的数据组合成一个有机的整体,以便于引用。这些数据是相互联系的。如一个学生的有关信息:,可采用结构体数据结构描述上述信息。,13.1 结构体,例如:,struct studentint num;char name20;char sex;int age;char addr30;;,定义一个结构体类型的一般形式为:,struct 结构体名 成员表列;,对各成员都要进行类型说明;成员名定名规则与变量名同。,是类型,不是变量名,11.1.1 结构体类型的声明,声明的一般形式为:struct 结构名成员1的说明;成员2的说明;成员n的说明;,struct medicinechar code;/*药品代号*/char name;/*药品名称*/float price;/*单价*/char place;/*产地*/stuct goods caption;/*来源地*/;,11.1.2 结构体变量,方法一:先定义结构体类型再定义变量名struct studentint num;char name20;char sex;int age;char addr30;;struct student student1,student2;,定义studet1和sudent2为struct student类型变量,不能只指定一个变量为“struct型”而不指定结构体名,有时,可用符号常量代表一个结构体类型,如:,#define STUDENT struct studentSTUDENTint num;char name20;char sex;int age;char addr30;,这样,可直接用STUDENT定义变量,如:STUDENT student1,student2;此时,不必再写关键字struct,方法二:在定义类型的同时定义变量,如:,struct studentint num;char name20;char sex;int age;char addr30;student1,student2;,一般形式是:struct 结构体名 成员表列 变量名表列;,结构体变量的存储,struct testint m110;char m2;float m3;double m4;aa;,sizeof(struct test)的值为2*10+1+4+8=33,一个结构体变量所占用内存空间的字节数可以用sizeof运算符求出,它的一般形式为:,sizeof(变量名或类型标识符);,几点说明:,1.类型与变量是不同概念,不要混淆;2.结构体中的成员,可以单独使用,其作 用与地位相当于普通变量;3.成员也可以是一个结构体变量;例如:,struct date int month;int day;int year;,Struct studentint num;char name20;int age;struct date birthday;student1,student2;,4.成员名可以与程序中的变量名相同,二者不代表同一对象。,11.1.3 结构体变量的引用与初始化,规则:1.不能将一个结构体变量作为一个整体进行赋值和输出;只能对其各个成员分别输出(引用形式为:结构体变量名.成员名)。printf(“.”,student1);printf(“%d”,student1.num);输出 10010,错!,正确!,2.若成员本身又属一个结构体类型,只能对最低级的成员进行赋值或存取以及运算。如:student1.birthday.year(下页续),(接上片),3.对成员变量可以象普通变量一样进行各种运算,如:sumage=student1.age+student2.age;4.可以引用成员的地址,也可以引用结构体变量的地址,如 scanf(“%d”,错!,输入student1.num的值,输出student1的首地址,结构体变量的初始化,(一)对外部存储类型的结构体变量初始化:struct studentlong int num;char name20;char sex;char addr20;a=9801,”Wang hong”,W,”2 Linggong Road”;main()printf(“No.:%ldnname:%snsex:%cnaddress:%sn”,a.num,a.name,a.sex,a.addr);,运行结果为:No.:9801name:Wang hongsex:Waddress:2 Linggong Road,(二)对静态存储类型的结构体变量初始化,如:,main()static struct studentlong int num;char name20;char sex;char addr20;a=9801,”Wang hong”,W,”2 Linggong Road”;printf(“No.:%ldnname:%snsex:%cnaddress:%sn”,a.num,a.name,a.sex,a.addr);,例题:设有三个候选人,每次输入一个得票的候选人的名字,要求最后输出各人得票结果。struct person char name20;int count;leader3=“Li”,0,”zhang”,0,”Liu”,0;main()int i,j;char leader_name20;for(i=1;i=10;i+)scanf(“%s”,leader_name);for(j=0;j3;j+)if(strcmp(leader_name,leaderj.name)=0)leaderj.count+);for(i=0;i3;i+)printf(“%5s:%dn”,leaderi.name,leaderi.count);,11.1.5 结构体指针,定义格式为:struct 结构名结构体;struct 结构名 变量名1,变量名2,变量名n,*指针变量名1,*指针变量名2,*指针变量名n;指针变量名1=,1指向结构变量的指针变量,结构体变量的指针:是该结构体变量所占居的内存段的起始地址。例如:main()struct studentlong int num;char name20;char sex;struct student stu_1;struct student*p;p=&stu_1;,stu.num=9901;strcpy(stu_1.name,”Li Min”);stu_1.sex=W;printf(“No.:%ldnname%snsex:%cn”,stu_1.num,stu_1.name,stu_1.sex);printf(“nNo.:%ldnname%snsex:%cn”,(*p).num,(*p).name,(*p).sex);,引用结构体成员的三种形式:结构体变量名.成员名(*p).成员名p-成员名,指向运算符。其优先级高于自增、自减运算符,试分析以下运算:p-n 得到p指向的结构体变量中的成员n的值p-n+得到p指向的结构体变量中的成员n的值,用完后使它加1;+p-n 得到p指向的结构体变量中的成员n的值 使其先加1,2指针变量的引用,指向结构数组的指针变量,我们知道,数组和指针有密切的关系。同样,结构数组和结构数组指针也紧密相关。当定义一个结构数组后,我们还可以定义一个结构指针变量,使该指针变量指向这个数组。那么,程序中既可以利用数组下标访问一个数组元素,也可以通过指针变量的操作来存取结构数组元素。例如,定义一个结构类型worker和结构数组class:struct workerchar name20;float salary;int age;int num12;struct worker class10;struct worker*pa;pa=/*指针变量pa指向结构体数组的首地址*/,定义stu1是类型为struct student的结构体变量,pa是可以指向该类型对象的指针变量。但应该注意的是:经过上面的定义,此时pa尚未指向任何具体的对象。为使pa指向stu1,必须把stu1的地址赋给pa:pa=注意,在定义了*pa之后,应该知道:(1)*pa不是结构变量,因此不能写成pa.ave,必须加上圆括号(*ps).ave。为此,C语言引入一个指向运算符“”,连接指针变量与其指向的结构体变量的成员。“”为间接成员运算符,其一般引用的格式为:指针变量名结构成员名说明:运算符“”是由连字符和大于号组成的字符序列,它们要连在一起使用。C语言把它们作为单个运算符使用。所以我们可以将(*ps).ave改写为psave。(2)pa只能指向一个结构体变量,而不能指向结构体变量中的一个成员。(3)指向运算符“”的优先级别最高,,11.2 用结构体指针操作链表,11.2.1 链表概述 链表是将若干数据项按一定规则连接起来的表,链表中的每个数据称为一个结点。即链表是由称为结点的元素组成的,结点的多少根据需要确定。链表连接的规则是:前一个结点指向下一个结点;只有通过前一结点才能找到下一个结点。因此,每个结点都应包括两方面的内容:(1)数据部分,该部分可以根据需要由多少个成员组成,它存放的是需要处理的数据。(2)指针部分,该部分存放的是一个结点的地址,链表中的每个结点通过指针连接在一起。我们可以对链表进行归类:当然一个链表必须要知道其表头的头指针。如果一个链表中的结点只有一个指向其他结点的指针,则称为单链表;若结点有两个指向其他结点的指针,则称为双链表。,单链表结点的类型定义:struct 结构名 数据成员列表;struct 结构名*指针名;结构体中struct 结构名*指针名;表示成员指针所指对象的类型就是自身结点类型。,例如:struct stureint num;char name10;char sex;struct stu_node struct sture s1;/*定义结点数据域*/struct stu_node*next;/*定义结点指针域*/struct stu_node*head;/*定义可指向结点的指针变量head*/,11.2.2 动态内存管理函数,1malloc(size)函数函数原型:void*malloc(unsigned size)功能:在内存的动态存储区的自由内存部分中分配一个长度为size字节的连续内存区域。返回值:如果执行成功,它的返回值是该内存区域首字节的指针;如果执行不成功,它的返回值是0,即NULL。2calloc(n,size)函数函数的原型:void*calloc(unsigned n,unsigned size)功能:在内存的动态存储区的自由存储部分中分配n个长度为size字节的连续内存区域。返回值:如果执行成功,它的返回值为连续内存区域首字节的指针;如果执行不成功,它的返回值是0,即NULL。3free(ptr)函数函数原型为:void free(void*ptr)功能:释放由ptr指向的内存,被释放的内存还回给系统,并成为自由内存。ptr是指针或指针变量,它的值通常是由malloc或calloc函数调用时返回的值。,例题:假设学生链表的接点类型为struct st,请编写函数struct st*(struct st*head,long key),功能是从头指针为head的学生链表中删除学号为key的那个结点 struct stlong num;char name12;int scorestruct st*next;,struct st*del(struct st*head,long key)struct st*p,*pre;p=head;while(p-num!=key)/*返回结点删除以后链表的头指针*/,11.2 共用体,11.2.1 共用体变量的定义共用体:使几个不同的变量共占同一段内存的结构,称为“共用体”类型的结构。“共用体”类型变量的定义形式为:union 共用体名 成员表列 变量表列;例如:union data int i;char ch;float f;a,b,c;,或union dataint i;char ch;float f;union data a,b,c;,或union int i;char ch;float f;a,b,c;,直接定义,先定义类型,11.2.2 共用体变量的引用,注意:只能引用共用体变量中的成员,不能引用共用体变量本身。如:可以引用 a.i(引用共用体变量中的整型变量i)a.ch(引用共用体变量中的字符变量ch)a.f(引用共用体变量中的实型变量f)不能只引用共用体变量,如:printf(“%d”,a);,错误!,总结:共用体类型数据的特点,1.每一瞬时只有一个成员起作用;2.共用体变量中起作用的成员是最后一次存放的成员;3.共用体变量的地址和它的各成员的地址都是同一地址;4.不能对共用体变量名赋值,也不能企图引用变量名来 得到成员的值,又不能在定义共用体变量时对它初始化。5.不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可使用指向共用体变量的指针;6.共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。而结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。,注意共用体类型变量与结构体类型变量的区别,结构体类型变量所占内存长度是各成员占的内存长度之和。共用体类型变量所占内存长度等于最长的成员的长度。,11.3 枚举类型,若一个变量只有几种可能的值,可以定义为枚举类型。所谓“枚举”是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。定义方法:先定义枚举类型 enum weekday sun,mon,tue,wed,thu,fri,sat;再用此类型定义变量,如:enum weekday workday,week_end;或直接定义枚举变量。如:enum weekday sun,mon,tue,wed,thu,fri,sat workday,week_end;,说明:,枚举元素为常量,不是变量,故不能对它们赋值枚举常量有值。如上面定义中,sun、mon、tue sat的值依次为0、1、27也可改变枚举元素的值,在定义时指出,如:enum weekdaysun=7,mon=1,tue,wed,thu,fri,sat;枚举值可用来作判断比较,如:if(workday=mon)if(workdaysun)一个整数不能直接赋值给一个枚举变量,应先进行强制类型转换才能赋值,如:workday=(enum weekday)2;(相当于将序号为2的枚举元素值赋给workday,即:workday=tue;,例 枚举类型运用,main()enum workdaysun=7,mon=1,tue,wed,fri,thu,sattoday,tomorrow;today=sat;tomorrow=today%7+1;printf(“today=%dn”,today);if(tomorrow=7)printf(“tomorrow is Sunday!n”);,today=6tomorrow is Sunday!,scanf(“%s”,11.4 typedef类型声明,C语言允许用typedef说明一种新的数据类型名,其一般形式为:typedef 类型名1 类型名2;其中,关键字typedef用于给已有类型重新定义新类型名,类型名1为系统提供的标准类型名或是已定义过的其他类型名;类型名2为用户自定义的新类型名。它往往可以简化程序中变量的类型定义。例如:typedef int WORD;定义WORD等价于数据类型int,此后,就可用WORD对变量进行类型说明,如:WORD a,b,c,*pa;实际上,C编译程序把上述变量作为一般的整型变量处理。在这种情况下,变量所表示的含义较为清楚,从而增加了程序的可读性。,对typedef的几点使用说明如下:(1)typedef不能用于变量的定义,只能对已存在的类型增加新的类型名,而不能定义新的类型。(2)从表面上看,typedef与#define的使用方式十分相似,但两者本质上是不同。例如:typedef int COUNT#define COUNT int typedef定义的是一种新的数据类型,类型名为COUNT,它是系统标准类型(int)的别名,在预编译时,编译器会将COUNT与int作为同一个类型来处理。#define定义的是一个宏,宏名为COUNT,在预编译时,编译器将进行宏替换,把字符串COUNT替换为字符串int。,