510 likes | 657 Views
结 构 体. 共 用 体. 枚举类型. 制作: 程世杰 哈理工 计算中心. Chengsj@hrbust.edu.cn. 结构体的定义及引用 结构体数组 结构体与指针 用指针处理链表 共用体的概念 枚举类型 用 typedef 定义类型. 重 点. 结构体. 难 点. 结构体类型 链 表. 结 构 体. 如一个学生的信息:学号 、 姓名、性别、年龄、成绩、地址等. int num;. C语言提供了这种数据结构: 允许用户将不同类型的数据组合成一个有机的整体,这些数据互相联系 ;这种数据结构称为 结构体 ( structure ) 。.
E N D
结 构 体 共 用 体 枚举类型 制作:程世杰 哈理工 计算中心 Chengsj@hrbust.edu.cn
结构体的定义及引用 结构体数组 结构体与指针 用指针处理链表 共用体的概念 枚举类型 用typedef定义类型
重 点 结构体 难 点 结构体类型 链 表
结 构 体 如一个学生的信息:学号、姓名、性别、年龄、成绩、地址等 int num; C语言提供了这种数据结构:允许用户将不同类型的数据组合成一个有机的整体,这些数据互相联系;这种数据结构称为结构体(structure)。 char name[20]; char sex; int age; float score; char addr[30];
结构体类型名 一. 定义一个结构体类型: struct结构体类型标识符 { 类型标识符1 成员名1; 类型标识符2 成员名2; ………… 类型标识符n 成员名n; } structstudent { int num; char name[20]; char sex; int age; float score; char addr[30]; } 成员表列 ; ; 每个成员也叫结构体中的一个域
? Question: 定义一个反映学生基本情况的结构类型,用以存储学生的相关信息。 struct date /*日期结构类型:由年、月、日三项组成*/ {int year; int month; int day; }; struct date { int year,month,day; }; struct score /*成绩结构类型: 共4项组成*/ {char no[7]; int score1; int score2; int score3; }; struct m { int x; float y; char z; } ; 定义一个结构体m,含三个不同类型的成员
二. 定义结构体变量 structstudent { int num; char name[20]; float score; }; struct 结构体类型标识符变量名列表; struct student y; int x;
num 2字节 name … 20字节 score 4字节 结构体变量定义的三种方法 ①、先定义结构体类型再定义变量名 在定义了结构体变量后,系统会为之分配内存单元。 structstudent { int num; char name[20]; float score; }; struct student stu1,stu2; 结构体类型名 结构体变量名 stu1 stu2 sizeof(stu1)= =26 4 2+ 20+
②、在定义结构体类型的同时定义变量 struct结构体类型标识符 { 类型标识符1 成员名1; 类型标识符2 成员名2; ………… 类型标识符n 成员名n; }变量名列表; structstudent { int num; char name[20]; float score; }stu1, stu2; ③、直接定义结构体类型变量 (可省略结构体类型名) struct { 类型标识符1 成员名1; 类型标识符2 成员名2; …… …… 类型标识符n 成员名n; }变量名列表; struct { int num; char name[20]; float score; }stu1, stu2;
三. 结构体变量的引用 结构体变量名.成员名 “.”成员运算符,在所有的运算符中优先级最高。 structstudent { int num; char name[20]; float score; }stu1, stu2; stu1.num=10001; stu1.score=95.5; stu1.name ="Li Ming"; strcpy(stu1.name, "Li Ming"); struct m { int x; float y; char z; }t; t.x=12; t.y=12.5; t.z=’f’; printf(“%d,%f,%c”,t.x,t.y,t.z);
说 明: 1、区分类型与变量: a、只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算; b、在编译时,对类型不分配内存空间,只对变量分配空间。 2、不能将一个结构体变量作为一个整体进行输入输出; 只能对结构体中的各个成员分别进行输入输出 printf("%d%s%f", stu1.num, stu1.name, stu1.score); gets(stu1.name); scanf("%d", &stu1.num); printf("%d%s%f", stu1); scanf("%d%s%f", &stu1);
3、只有在对结构体变量赋值或作为函数参数时才可以对一个结构体变量进行整体操作;(赋值时要求具有相同结构)3、只有在对结构体变量赋值或作为函数参数时才可以对一个结构体变量进行整体操作;(赋值时要求具有相同结构) stu2.num=stu1.num; strcpy(stu2.name, stu1.name); stu2.score=stu1.score; stu2=stu1; void fun(struct studentpx) { } 结构体变量 fun(stu1); 4、对成员变量可以像普通变量一样进行各种运算 sum=stu1.score+stu2.score; stu1.age++;
5、如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级的找到最低一级的成员。只能对最低一级的成员进行赋值、存取或运算;5、如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级的找到最低一级的成员。只能对最低一级的成员进行赋值、存取或运算; 结构体类型的嵌套定义 struct date {int year; int month; int day; }; struct std_info {char no[7]; char name[9]; char sex; struct date birthday; }stu1; stu1.birthday.year=2002;
6 成员也可以是指向本结构体类型的指针变量 struct stu_info { int num; char name[20]; float score; struct stu_info *next; }; 28 sizeof(struct stu_info)=? struct student {int num; char name[20]; char sex; struct date birthday; char addr[30]; }; struct date { int year; int month; int day; }; 59 sizeof(struct student)=?
7、在定义结构体变量的同时,可以进行初始化。7、在定义结构体变量的同时,可以进行初始化。 struct student { int num; char name[20]; char sex; char addr[30]; }stu={15001, “Wang", 'M', "123 Road harbin"}; struct student { int num; char name[20]; char sex; char addr[30]; }stu; stu.num=15001; strcpy(stu.name,”Wang”); sex=‘M’; strcpy(stu.addr,”123 road Ha”);
结构体的定义,哪个不正确? • struct date • { int month; • int day; • int year; • } • struct date tt; B. #define DATE struct date DATE { int month; int day; int year; } tt; C. struct { int month; int day; int year; } tt; D. struct date { int month; int day; int year; } tt;
sizeof(test)= printf(“%d\n”, ); 再定义一个与test同类型的变量fac printf(“%c\n”, ); printf(“%f\n”, ); 2 1 struct menu { char a; int b; long c; float d; }mu={‘k’,12,906,2.5}; struct m {int x; int y; float z; char p; }test; mu.b structm fac; mu.a mu.d 9
结构体数组 数组的每个元素都是结构体类型的数据,它们分别包含各个成员项。 1、先定义结构体类型,再定义结构体数组 struct student { int num; float score; }; struct studentstu[30]; 2、定义结构体类型的同时定义结构体数组 struct student { int num; float score; }stu[30]; struct { int num; float score; }stu[30]; 3、直接定义结构体数组
num num name name 25B stu[0] sex sex age age stu[1] 形式一: struct student { int num; char name[20]; char sex; int age; }; struct student stu[2]; 形式二: struct student { int num; char name[20]; char sex; int age; }stu[2];
结构体数组的初始化 一般形式: struct 结构体类型标识符 { 类型标识符1 成员名1; 类型标识符2 成员名2; ………… 类型标识符n 成员名n; }结构体数组={ {数组元素0的各个初值}, {数组元素1的各个初值}, ……}; 例: struct student { int num; char name[20]; float score; }stu[3]={{10101,"Li Ming",88},{10102,"Zhang Jin",92}, {10103, "Wang Lin", 98.5}};
说明: 1、初始化时,数组元素的个数可以不指定,系统会根据初值的结构体常量的个数来确定数组元素的个数; struct student { int num; char name[20]; float score; }stu[ ]={{10101,"Li Ming",88},{10102,"Zhang Jin",92}, {10103, "Wang Lin", 98.5}}; 或: struct student stu[ ]={{10101,"Li Ming", 88}, {10102,"Zhang Jin",92}, {10103, "Wang Lin", 98.5}}; 2、数组中各个元素的初值用大括号{、}括起来,同一数组元素的各个成员变量的初值用逗号分隔。
结构体数组应用举例 若有如下定义: ① 要给stu[1]输入18、990001、“wang zhi gang",写出所用到的语句; struct aa { long num; char name[20]; }; stu[1].age=18; stu[1].first.num=990001; strcpy(stu[1].first.name, “wang zhi gang”); struct bb { int age; struct aa first; }stu[2]; ②、要把‘w'、‘z'、‘g'变成大写,如何实现? stu[1].first.name[0]-=32; stu[1].first.name[5]-=32; stu[1].first.name[9]-=32; ③、如何将stu[1]中的名字复制到stu[0]中的相应位置上? strcpy(stu[0].first.name,stu[1].first.name);
#include “stdio.h” struct cmplx {int x; int y; }cnum[2]={1,3,2,7}; main( ) { printf(“%d\n”,cnum[0].y*cnum[1].x); } cnum[0].x=1 cnum[0].y=3 cnum[1].x=2 cnum[1].y=7 6
结构体与指针 结构体变量的指针就是该变量所占据的内存段的起始地址。 指向结构体变量的指针变量 1、指向结构体变量指针的定义: struct student { int num; char name[20]; float score; }; struct studentstu, *p; p中存放着结构体变量stu在内存中的首地址 p=&stu; 注意:不能用指向结构体变量的指针指向该结构体变量的某个成员。 int *ip; ip=&stu.num; p=&stu.num;
2、访问结构体成员变量的三种方法: ①、stu.num、stu.name、stu.score ②、(*p).num、(*p).name、(*p).score ③、p->num、p->name、p->score 3、说明: ①、“->”为指向运算符,是优先级最高的运算符; ②、成员运算符“.”的优先级高于指针运算符“*”,因此采用 “(*p).成员名” 形式时,括号不能省略; ③、注意以下几种运算: p->num 得到p指向的结构体变量中的成员num的值 p->num++ 得到p指向的结构体变量中的成员num的值,用完后该值加1 使p指向的结构体变量中的成员num的值加1 ++p->num
#include<stdio.h> main() {struct student {long num; char name[20]; char sex; float score; }; struct student stu_1; struct student *p; p=&stu_1; stu_1.num=2003001; strcpy(stu_1.name,”Li Tao”); stu_1.sex=‘M’; stu_1.score=90.5; printf(“%ld,%s\n”,stu_1.num,stu_1.name); printf(“%%c,%.2f\n”,stu_1.sex,stu_1.score); } printf(“%ld,%s\n”,p->num,p->name); printf(“%ld,%s\n”,(*p).num,(*p).name); printf(“%%c,%.2f\n”,p->sex,p->score); printf(“%%c,%.2f\n”,(*p).sex,(*p).score);
指向结构体数组的指针 指向结构体变量的指针变量,可以指向结构体变量,也可以指向同类型的结构体数组的元素。 p=&stu[3]; struct student { int num; char name[30]; float score; }stu[30], *p; p=stu; p=&stu[2].num; p只能指向一个struct student类型的数据(某个元素的的起始地址),不能指向一个成员变量。 注 意 ①、若p的初值为stu,p+1后指向下一元素的起始地址; (++p)->num 先使p自加1,然后得到它指向元素的num成员值 ++p->num 使p指向的结构体变量中的成员num的值加1 (p++)->num 先得到p->num的值,然后使p自加1,指向stu[1] p->nunm++ 使p指向的结构体变量中的成员num的值加1 ②、若p已定义为指向结构体类型的数据,则p只能指向相应类型的变量或数组元素,不能指向数组元素中的某一成员;
#include<stdio.h> struct s {int x,*y; }*p; int d[5]={10,20,30,40,50}; struct s array[5]={100,&d[0], 200,&d[1],300,&d[2], 400,&d[3],500,&d[4]}; array[0] d[0] array[1] d[1] array[2] d[2] array[3] d[3] array[4] d[4] main() {p=array; printf(“%d\n”,p->x); printf(“%d\n”,(*p).x); printf(“%d\n”,(++p)->x); 200 100 printf(“%d\n”,p->x++); 200 100 printf(“%d\n”,++(*p->y)); 21 10 printf(“%d\n”,*p->y); printf(“%d\n”,++*p->y); 22 10 printf(“%d\n”,*(*p).y); printf(“%d\n”,*++p->y); } 30 101 printf(“%d\n”,++p->x);
动态存储分配 数组的长度是预先定义好的,在整个 程序中固定不变。C语言中不允许定义 动态数组。 实际的编程中,往往会发生这种情况, 即所需的内存空间取决于实际输入的数 据,而无法预先确定。对于这种问题,用 数组的办法很难解决。为了解决上述问题 ,C语言提供了一些内存管理函数,这些 内存管理函数可以按需要动态地分配内存 空间,也可把不再使用的空间回收待用, 为有效地利用内存资源提供了手段。
常用的内存管理函数 分配内存空间函数malloc 调用形式: (类型说明符*) malloc(size) 功能:在内存的动态存储区中分配一块长度为"size"字节的连续区域。函数的返回值为该区域的首地址。 pc=(char *)malloc(100); 分配内存空间函数calloc 调用形式: (类型说明符*)calloc(n,size) 功能:在内存动态存储区中分配n块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。 (类型说明符*)用于强制类型转换。 ps=(struct stu*)calloc(2,sizeof(struct stu)); 释放内存空间函数free free(pc); 调用形式: free(void *ptr);
Number=102 Name=xiaoxiao Sex=M Score=62.5 分配一块区域,输入一个学生数据 main() { struct stu { int num; char *name; char sex; float score; } *ps; ps=(struct stu*)malloc(sizeof(struct stu)); ps->num=102; ps->name=“Xiao xiao"; ps->sex='M'; ps->score=62.5; printf("Number=%d\nName=%s\n",ps->num,ps->name); printf("Sex=%c\nScore=%f\n",ps->sex,ps->score); free(ps); } sizeof(struct stu)=? 申请内存空间 使用内存空间 释放内存空间
链表的概念 问题的引出 关键词:数组、n个元素、连续内存空间 链式结构 数据结构中一种标记,相邻结点间连接方式 链表 具有表头和表尾的链接结构 head 1110 1460 1680 1200 1110 表头 表尾
例如,一个存放学生学号和成绩的结点为以下结构:例如,一个存放学生学号和成绩的结点为以下结构: struct stu { int num; int score; struct stu *next; } 前两个成员项组成数据域,后一个成员项next构成指针域,它是一个指向stu类型结构的指针变量。 链表的基本操作: 1.建立链表; 2.结构的查找与输出; 3.插入一个结点; 4.删除一个结点;
p1 head head p1 p2 p2 p1 p1 head head p2 p2
建立一个单向动态链表 #define NULL 0 #define LEN sizeof(struct student) struct student {long num; int score; struct student *next; } int n; struct student *creat(void) {struct student *head; struct student *p1,*p2; n=0; head=NULL; p1=p2=(struct student *)malloc(LEN); scanf(“%ld,%d”,&p1->num,&p1->score); while(p1->num!=0) {n++; if(n==1)head=p1; else p2=p1; p1=(struct student *)malloc(LEN); scanf(“%ld,%d”,&p1->num,&p1->score); } p2->next=NULL; return(head); } 程序演示
i ch f 2000 共用体的概念 有时候需要将几种不同类型的变量存放到同一段内存单元中。 可以把一个整型变量、一个字符型变量、一个实型变量放在同一个地址开始的内存单元中。即使几个不同的变量共占同一段内存空间。 所谓“共用体(union)”是指使几个不同的变量共占同一段内存的数据类型。 共用体的定义 1、先定义共用体类型标识符,再定义变量 union共用体类型标识符 { 类型标识符1 成员名1; 类型标识符2 成员名2; …… …… 类型标识符n 成员名n; }; union data { int i; char ch; float f; }; union 共用体类型标识符变量名表; union data a, b, c;
2、直接定义共用体变量 union共用体类型标识符 { 类型标识符1 成员名1; 类型标识符2 成员名2; …… …… 类型标识符n 成员名n; }变量名表; union data { int i; char ch; float f; }a, b, c; union { int i; char ch; float f; }a, b, c; 3、共用体与结构体的定义形式相似,但含义不同: 结构体变量所占的内存长度等于各成员所占的内存长度之和。(每个成员分别占有自己的内存) 共用体变量所占的内存长度等于最长的成员的长度。(每个成员分别占有自己的内存) 4 sizeof(union data)=
共用体变量的引用 1、引用形式为: 共用体变量名.成员名 union data { int i; char ch; float f; }a, b, c; 引用共用体变量a中的整型变量i a.i 引用共用体变量a中的字符变量ch a.ch a.f 引用共用体变量a中的实型变量f 2、注意: 不能直接引用共用体变量,而只能引用共用体变量的成员 printf("%d", a); printf("%d", a.i); printf("%d", a.ch); 3、共用体类型数据的特点: ①、同一内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种;
也就是说,每一瞬时只有一个成员起作用,其它的成员不起作用,即不是同时都存在和起作用。也就是说,每一瞬时只有一个成员起作用,其它的成员不起作用,即不是同时都存在和起作用。 ②、共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用; a.i=1; a.ch='a'; a.f=1.5; ③、共用体变量的地址和它的各成员的地址是同一地址; &a, &a.i, &a.ch, &a.f都是同一地址 ④、不能对共用体变量名赋值;也不能企图引用变量名来得到成员的值;不能在定义共用体变量时进行初始化; union { int i; char ch; float f; }a={1, 'a', 1.5}; a=1; m=a; ⑤、不能把共用体变量作为函数参数,也不能让函数带回共用体变量,但可以使用指向共用体变量的指针;
⑥、共用体作为一种数据类型,可以在定义其它数据类型中使用。可以将结构体变量的某一成员定义为共用体类型,也可以定义共用体数组。⑥、共用体作为一种数据类型,可以在定义其它数据类型中使用。可以将结构体变量的某一成员定义为共用体类型,也可以定义共用体数组。 例:设有若干人员的数据,其中有学生和教师。学生的数据包括:姓名、学号、性别、职业、班级。教师的数据包括:姓名、教师号、性别、职业、职务。现要求把它们放在同一表格中。 int num; union { int class; char position[10]; }category; char name[20]; char sex; char job;
struct person { int num; char name[20]; char sex; char job; union { int class; char position[10]; }category; }per[30]; scanf("%c", &per[i].job); if (per[i].job=='s') scanf("%d", &per[i].category.class); else if (per[i].job=='t') gets(per[i].category.position);
12,ZZ,89.500000 #include<stdio.h> #include<string.h> struct un { int i; char name[20]; float score; } union main() {struct un u; u.i=12; strcpy(u.name, “ZZ”); u.score=89.5 printf( “%d,%s,%f\n”, u.i,u.name,u.score); } union 0, ,89.500000
枚举类型 枚举类型的定义 所谓“枚举”是指将变量的所有取值一一列举出来,变量的值只限于列举出来的值的范围内。 枚举类型变量的定义: 1、先定义枚举类型标识符,再定义变量 enum枚举类型标识符{枚举元素1, 枚举元素2, ……, 枚举元素n}; enum枚举类型标识符变量列表; enum person{Man, Woman}; enum person x; enum weekday{Sun, Mon, Tue, Wed, Thu, Fri, Sat}; enum weekday workday, weekend;
2、直接定义枚举变量 enum枚举类型标识符{枚举元素1, 枚举元素2, ……, 枚举元素n}变量列表; enum person{Man, Woman}x; enum weekday{Sun, Mon, Tue, Wed, Thu, Fri, Sat} workday, weekend; enum BOOL{FALSE, TRUE}; 3、说明: Sun、Mon、……、Sat等称为枚举元素或枚举常量,它们是用户自定义标识符。这些标识符并不自动的代表什么含义,用什么标识符代表什么含义,完全由程序员决定,并在程序中作相应的处理。
枚举变量的引用 1、在C编译时,对枚举元素按常量处理,故称枚举常量。它们不是常量,不能对它们赋值。 Sun=0; TRUE=1; 2、枚举元素作为常量,它们是有值的。C语言编译时按定义时的顺序使它们的值为0、1、2、…… enum weekday{Sun, Mon, Tue, Wed, Thu, Fri, Sat}workday; workday=Mon; printf("%d", workday); 3、可以在定义时改变枚举元素的值 enum weekday{Sun=7, Mon=1, Tue, Wed, Thu, Fri, Sat}workday; enum weekday{Sun=7, Mon, Tue, Wed=0, Thu, Fri, Sat}; 4、枚举值可以用来做判断比较 if (workday==Mon) …… if (workday>Sun) ……
5、一个整数不能直接赋给一个枚举变量 workday=2; workday=(enum weekday)2; workday=(enum weekday)(x-y); 例:从键盘输入0~6的任意整数,0表示星期日,1~6分别表示星期一到星期六,要求编写程序输出对应的英文名称。 enum weekday{Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}week; scanf("%d", &i); switch(i) { case 0: week=Sunday; break; case 1: week=Monday; break; case 2: week=Tuesday; break; case 3: week=Wednesday; break; case 4: week=Thursday; break; case 5: week=Friday; break; case 6: week=Saturday; break; } switch(week) { case Sunday: …… break; case Monday:…… break; case Tuesday: …… break; case Wednesday: …… break; case Thursday: …… break; case Friday: …… break; case Saturday: …… break; }
类型标识符的自定义 1、除了可以直接使用C提供的标准类型名(int、char、float等)和自己定义的数据类型(结构体、共用体、指针、枚举类型)外,还可以用typedef声明新的类型名来代替已有的类型名。 int i, *p; typedef intINTEGER INTEGER i, *p; float x, y; typedef floatREAL REAL x, y; 2、定义类型标识符的方法: float x; ①、按定义变量的方式写出定义体: ②、将变量名换成新的类型标识符: REAL x; typedefREAL x; ③、在最前面加typedef: 3、例:
⑴、定义含有10 个元素的整型数组: ①、先按照定义数组的形式书写:int a[10]; ②、将变量名a换成自己指定的类型名:int ARRAY[10]; ③、在前面加上typedef:typedef int ARRAY[10]; int a[10], b[10]; ARRAY a, b; ⑵、定义字符指针类型: ①、char *p; STRING p, ps[10]; ②、char *STRING; ③、typedef char *STRING; ⑶、声明结构体类型: struct { int num; char name[20]; float score; }stu; struct { int num; char name[20]; float score; }STUDENT; typedef struct { int num; char name[20]; float score; }STUDENT; STUDENT stu[30], *p;
4、说明: ①、自定义类型标识符可以是任意合法的标识符,习惯上常把用typedef定义的标识符用大写字母表示,以便与系统提供的标准类型标识符相区别; ②、用typedef只能为已经存在的数据类型标识符另起一个新名,而不能创造一种新的数据类型; typedef int ARRAY[10]; ③、typedef与#define有相似之处: typedef int INTEGER #define INTEGER int 作用都是用INTEGER代表int。 #define是在编译之前进行处理的,只做简单的字符替换。 typedef是在编译时处理的,实际上并不是做简单的字符替换。 如:typedef int ARRAY[10] 并不是用“ARRAY[10]”去代替int,而是采用如同定义变量的方法那样来声明一个类型。
⑤、当不同的文件中涌到同一类型数据时(结构体、共用体等),常用typedef声明一些数据类型,把它们单独放在一个文件中,需要用时用#include命令把它们包含进来;⑤、当不同的文件中涌到同一类型数据时(结构体、共用体等),常用typedef声明一些数据类型,把它们单独放在一个文件中,需要用时用#include命令把它们包含进来; ⑥、使用typedef有利于程序的通用和移植; typedef int INTEGER typedef long INTEGER