480 likes | 610 Views
计算机程序设计. ch 10 结 构 体. 1 结构体 (Structure) 定义与引用. 结构体概述 结构体类型的声明 结构体变量的定义 结构体变量的初始化 结构体变量的引用. 结构体概述. 结构体 将不同类型的数据组合成一个整体 用来表示简单类型无法描述的复杂对象 可以用结构体来定义用户自己的数据结构 举例 描述学生信息. 结构体类型的声明. 一般形式 struct [ 结构体名 ] { 成员表列 }; “ 成员表列 ” 形式 类型 成员名 ; 类型 成员名 ; . 结构体类型声明的说明 (1).
E N D
计算机程序设计 ch 10 结 构 体
1 结构体 (Structure)定义与引用 • 结构体概述 • 结构体类型的声明 • 结构体变量的定义 • 结构体变量的初始化 • 结构体变量的引用
结构体概述 • 结构体 • 将不同类型的数据组合成一个整体 • 用来表示简单类型无法描述的复杂对象 • 可以用结构体来定义用户自己的数据结构 • 举例 • 描述学生信息
结构体类型的声明 • 一般形式 • struct [结构体名] { 成员表列 }; • “成员表列”形式 类型 成员名; 类型 成员名; ... ...
结构体类型声明的说明 (1) • 声明了一种类型,而不是定义变量 • 结构体名可以没有,但是这样就无法再次使用该结构体类型了 • 成员表列中是成员(Member)的定义 • 成员的定义形式与变量定义相同 • 成员类型可以是另一结构体类型,但不可直接或间接递归嵌套(自身); • 成员表列不可为空,至少要有一个成员
结构体类型声明的说明 (2) • 注意{}后有分号 • 同一结构体的成员不能重名 • 不同结构体的成员可以重名; • 结构体成员和其他变量可以重名; • 结构体类型与其成员或其他变量可重名 • 结构体类型名称是 struct 结构体名, 注意struct关键字不能省略
结构体类型声明的说明 (3) • 即使两个结构体声明中的成员类型、名称、顺序都完全一致,它们也是不同的类型 • 结构体类型也要“先声明,后使用” • 如果结构体类型声明在函数内部,则该函数之外无法引用此结构体类型 • 一般把结构体类型声明放到文件最前面 • 也可以把结构体类型声明放在头文件里
结构体类型的声明举例 struct student { unsigned num; /* 学号 */ char name[20]; /* 姓名 */ char sex; /* 性别 */ unsigned age; /* 年龄 */ float score; /* 分数 */ char addr[50]; /* 地址 */ };
结构体变量的定义 (1) • 先声明结构体类型再定义变量 struct student { unsigned num; char name[20]; char sex; unsigned age; float score; char addr[50]; }; //定义类型 struct student stu1, stu2; //定义变量
结构体变量的定义 (2) • 在声明结构体类型的同时定义变量a struct student { unsigned num; char name[20]; char sex; unsigned age; float score; char addr[50]; } stu1, stu2;
结构体变量的定义 (3) • 直接定义无名结构类型变量 struct { unsigned num; char name[20]; char sex; unsigned age; float score; char addr[50]; } stu1, stu2;
结构体声明和变量定义举例 struct date { int year, month, day; }; struct student{ unsigned num; char name[20]; char sex; struct date birthday; float score; } stu1, stu2;
结构体变量的初始化 • 按照成员的顺序和类型对成员初始化 struct date date1 = {1990, 10, 20}; struct student stu = { 1001, /*unsigned num*/ "Tom", /*char name[20]*/ 'M', /*char sex*/ {1983, 9, 20},/*struct date birthday*/ 93.5 /*float score*/ };
结构体变量中成员的引用 • 一般形式 • 结构体变量名.成员名 • 成员运算符 . • 具有最高的优先级,自左向右结合 • 说明 • 结构体成员和同类型的变量用法相同 • 若成员类型又是一个结构体,则可以使用若干个成员运算符,访问最低一级的成员
结构体变量中成员的引用举例 struct student stu; ... ... scanf("%f", &stu.score); stu.num = 12345; stu.birthday.month = 11; stu.score = sqrt(stu.score) * 10; strcpy(stu.name, "Mike"); printf("No.%d:", stu.num);
结构体变量整体引用 • 结构体类型变量之间可以直接相互赋值 • 实质上是两个结构体变量相应的存储空间中的所有数据直接拷贝 • 包括复杂类型在内的所有结构体成员都被直接赋值,如字符串、结构体类型等 • 函数的实参和形参可以是结构体类型,并且遵循实参到形参的单向值传递规则 • 为了提高程序的效率,函数的参数多使用结构体类型指针
结构体变量整体引用举例 struct student stu1, stu2={1002, "Kate", 'F', {1981, 11, 4}, 89.0}; void print(struct student s) { printf("%d,%4s,%c,%d.%02d.%02d,%4.1f\n", s.num, s.name, s.sex, s.birthday.year, s.birthday.month, s.birthday.day, s.score); } void main () { stu1 = stu2; /* 直接赋值 */ print(stu1); /* 1002,Kate,F,1981.11.04,89.0 */ }
结构体变量整体引用举例 struct Person { char name[20]; int age; //... }; void main() { struct Person John; struct Person clone; strcpy(John.name,”John”); John.age=20; clone = John; //两个结构体变量具有了相同数据 }
2. 结构体数组 • 结构体数组的用法与基本类型数组类似 • 定义、初始化、引用 • 结构体数组可用于表示二维表格 • 举例 struct student s[10]; for (i=0; i<10; i++) scanf("%d,%s,%c,%d,%f", &s[i].num, s[i].name, &s[i].sex, &s[i].age, &s[i].score);
结构体数组初始化及应用举例 struct student stu[] = { {1001,"Tom",'M',{1980,1,2},85.5}, {1002,"Kate",'F',{1981,11,4},89.0}, {1003,"Mike",'M',{1980,3,5},95.5}}; for (i=0; i<3; i++) printf("%d,%4s,%c,%d.%02d.%02d,%4.1f\n", stu[i].num, stu[i].name, stu[i].sex, stu[i].birthday.year, stu[i].birthday.month, stu[i].birthday.day, stu[i].score);
结构体数组与二维表 struct student s[5]; 结构体元素 数组
3 指向结构体的指针 • 指向结构体的指针 • 定义、使用与其他基本类型指针类似 • 可以使用指向运算符(->)引用指针所指向的结构体的成员 • 指向运算符 -> • 结构体指针->成员名 • 具有最高的优先级,自左向右结合 • 若struct student stu, *p=&stu; 则stu.num、 (*p).num、 p->num等效
指向结构体数组的指针 • 指向结构体数组的指针 • 与指向其他基本类型数组的指针用法类似 • 注意相关运算符的结合方向和优先级 • 举例 struct student stu[10], *p=stu; ++p->num; /* 同++(p->num);*/ p++->num; /* 同(p++)->num;*/ (++p)->num; (p++)->num;
结构体指针作函数参数举例 void input(struct student *p) { scanf("%d %s %c %d.%d.%d %f", &p->num, p->name, &p->sex, &p->birthday.year, &p->birthday.month, &p->birthday.day, &p->score); } void main () { struct student stu[20]; int i; for (i=0; i<20; i++) input(stu+i); }
【例】分配一块存储区域,输入一个学生数据。【例】分配一块存储区域,输入一个学生数据。 ps=(struct stu*)malloc(sizeof(struct stu)); ps->num=102; ps->name="Zhang ping"; 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); } main() { struct stu { int num; char *name; char sex; float score; } *ps;
4 结构体的应用—链表 node struct node { int data; struct node *next; }; struct node *head; ai next data
链表的操作 • 链表的建立 • 从链尾到链头:新结点插入到链头 • 从链头到链尾:新结点插入到链尾 • 链表的遍历 • 删除结点 • 根据一定的条件,删除一个或多个结点 • 插入结点 • 根据一定的条件,把新结点插入到指定位置
ai-1 ai p 建立链表 (从链尾到链头) ① for(i=0; i<n; i++) ... head ④ head = p; ③ p->next = head; ② p = malloc(sizeof (struct node)); p->data = a[i];
p ai ^ ai-1 ^ q 建立链表 (从链头到链尾) ① for(i=0; i<n; i++) ⑤ p = q; ... ③ q->next = NULL; ④ p->next = q; ② q = malloc(sizeof (struct node)); q->data = a[i];
ai-1 ai ai+1 p 遍历链表 ① while(p) ③ p = p->next; ... ... ② printf("%d", p->data);
ai-1 ai ai+1 p q 删除结点 ① if(p->next满足删除条件) ② q = p->next; ④ free(q); ... ... ③ p->next = q->next;
ai ai+1 x p q 插入结点 ① if(p满足插入条件) ... ... ④ p->next = q; ③ q->next = p->next; ② q = malloc(sizeof (struct node)); q->data = x;
链表操作中需要注意的几个问题 • 注意考虑几个特殊情况下的操作: • 链表为空表 (head==NULL) • 链表只有一个结点 • 对链表的第一个结点进行操作 • 对链表的最后一个结点进行操作 • 最后一个结点的next指针应为NULL
例 用指针变量输出结构数组 struct stu { int num; char *name; char sex; float score; }boy[5]={ {101,"Zhou ping",'M',45}, {102,"Zhang ping",'M',62.5}, {103,"Liou fang",'F',92.5}, {104,"Cheng ling",'F',87}, {105,"Wang ming",'M',58}, }; main() { struct stu *ps; printf("No\tName\t\t\tSex\tScore\t\n"); for(ps=boy;ps<boy+5;ps++) printf("%d\t%s\t\t%c\t%f\t\n", ps->num, ps->name, ps->sex, ps->score); }
例 计算一组学生的平均成绩和不及格人数。用结构指针变量作函数参数编程。 struct stu { int num; char *name; char sex; float score;}boy[5]={ {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}, };
main() { struct stu *ps; void ave(struct stu *ps); ps=boy; ave(ps); } void ave(struct stu *ps) { int c=0,i; float ave,s=0; for(i=0;i<5;i++,ps++) { s+=ps->score; if(ps->score<60) c+=1; } printf("s=%f\n",s); ave=s/5; printf("average=%f\ncount=%d\n",ave,c); }
例 建立一个三个结点的链表,存放学生数据。为简单起见, 我们假定学生数据结构中只有学号和年龄两项。可编写一个建立链表的函数create。 #define NULL 0 #define TYPE struct stu #define LEN sizeof (struct stu) struct stu { int num; int age; struct stu *next; }; TYPE *creat(int n) { struct stu *head,*pf,*pb; int i; for(i=0;i<n;i++) { pb=(TYPE*) malloc(LEN); printf("input Number and Age\n"); scanf("%d%d",&pb->num,&pb->age); if(i==0) pf=head=pb; else pf->next=pb; pb->next=NULL; pf=pb; } return(head); }
共用体 • 概念:各成员共享同一段内存 —共用体 • 定义: union Data //定义类型 { int i; char ch; float f; } a ; //定义变量 union Data b, c; //定义变量
共用体 • 引用:只能引用共用体变量中的成员,而共用体变量不能整体引用。 union Data a,b; a.i=2; b.i=a.i; printf(“%d”, a); 错误。不能引用a。
共用体 • 特点: • 成员共享内存:只能放一个成员的值 • 共用体变量初始化只能对一个成员进行(初值表只有一个常量) • 最后一次赋值的成员有效(覆盖了以前所有成员的值) • 共用体变量的地址及其各成员地址都是同一地址 • 同类型共用体变量之间可互相赋值 • 共用体变量可作函数参数 • 可用作结构体成员;共用体成员也可以是数组或结构体。
共用体的应用 • 用于安排共享一段内存的数据 • 275页 例9.8 struct person{ int num; char name[10]; char sex; char birthday[12]; char addr[16]; union { char single[7]; char nmtel[20]; }mate; }ps[3];
枚举类型 • 概念: 当变量的值仅限于有限的可列举的范围时,使用枚举类型,可增强程序可读性,并减少变量取值误差。 • 定义形式: enum [枚举名] {枚举元素列表};//枚举元素由程序设计者自定义,但取值自动为0,1,2 … 例 enum Weekday {sun,mon,tue,wed,thu,fri,sat}; enum Weekday workday,weekend;
枚举类型 • 可自行指定枚举元素常量的值,例如 enum Weekday { sun=7,mon=1,tue,wed,thu,fri,sat,today }; // got 7 1 2 3 4 5 6 7 page277 例 9.9 9.10
用typedef定义类型别名 • 一般形式 • typedef <变量定义形式中变量名换成类型名> • 举例 • typedef float REAL; • typedef struct { int month; int day; int year; } DATE;
例 统计候选人选票 struct person { char name[20]; int count; }leader[3]={"Li",0,"Zhang",0, "Wang",0}; int main( ) { int i,j ; char leader_name[20]; for(i=1;i<=10;i++) { scanf("%s",leader_name); for(j=0;j<3;j++) if(strcmp(leader_name,leader[j].name)==0) leader[j].count++; } for(i=0;i<3;i++) printf("%5s:%d\n",leader[i].name,leader[i].count); }
实验作业 (指针) 257页:16, 17 (结构体)279页:1 2 3 The End 实验 化院12月3日(少年班12月5日) (结构体)279页:4, 5 6
http://www.c-faq.com/struct/align.html • --sizeof结构体 不等于 各成员字节数之和