420 likes | 526 Views
第十讲 结构体和共用体. 合肥师范学院计算机科学与技术系. 思考一个问题. 在程序里表示一个人(姓名、年龄、性别、 …… ),怎么表示? 想表示多个人呢? 如何用计算机程序实现下述表格的管理?. 表 1 某学校学生成绩管理表. 数组的解决方法. int studentId[30]; /* 最多可以管理 30 个学生 , 每个学生的学 号用数组的下标表示* / char studentName[10][30]; char studentSex[2][30];
E N D
第十讲 结构体和共用体 合肥师范学院计算机科学与技术系
思考一个问题 • 在程序里表示一个人(姓名、年龄、性别、……),怎么表示? • 想表示多个人呢? • 如何用计算机程序实现下述表格的管理? 表1 某学校学生成绩管理表
数组的解决方法 int studentId[30]; /* 最多可以管理30个学生, 每个学生的学 号用数组的下标表示*/ char studentName[10][30]; char studentSex[2][30]; int timeOfEnter[30]; /*入学时间用int表示*/ int scoreComputer[30];/*计算机原理课的成绩*/ int scoreEnglish[30]; /*英语课的成绩*/ int scoreMath[30]; /*数学课的成绩*/ int scoreMusic[30]; /*音乐课的成绩*/
令狐冲 男 1999 1 1999 林平之 男 2 1999 岳灵珊 女 3 1999 任莹莹 女 4 …… …… …… …… 90 83 72 82 78 92 88 78 89 72 98 66 78 95 87 90 …… …… …… …… 数组的解决方法 • 数据的内存管理方式
数组的解决方法 • 分配内存不集中,寻址效率不高 • 对数组进行赋初值时,容易发生错位 • 结构显得比较零散,不容易管理
1 2 3 4 林平之 岳灵珊 任莹莹 令狐冲 男 女 女 男 1999 1999 1999 1999 78 89 78 90 92 72 95 83 88 98 87 72 78 66 90 82 希望的内存分配图
1.15.1 结构的概念 • struct STUDENT { int studentID; /*每个学生的序号*/ • char studentName[10]; /*每个学生的姓名*/ • char studentSex[4]; /*每个学生的性别*/ • int timeOfEnter; /*每个学生的入学时间*/ • int scoreComputer; /*每个学生的计算机原理成绩*/ • int scoreEnglish; /*每个学生的英语成绩*/ • int scoreMath; /*每个学生的数学成绩*/ • int scoreMusic; /*每个学生的音乐成绩*/ }; • 结构:构造数据类型,把有内在联系的不同类型的数据统一成一个整体,使它们相互关联 • 结构又是变量的集合,可以单独使用其成员
1.15.2结构的定义 • 结构类型定义的一般形式为: struct 结构名 { 类型名 结构成员名1; 类型名 结构成员名2; 类型名 结构成员名n; }; 关键字struct和它后面的结构名一起组成一个新的数据类型名 结构的定义以分号结束,被看作一条语句
1.15.2结构定义示例 定义平面坐标结构: struct point { double x; double y; }; 虽然x、y的类型相同,也可以用数组的方式表示,但采用结构体描述整体性更强,增加了程序的可读性,使程序更清晰。
结构的嵌套定义 struct address{ char city[10]; char street[20]; int code; int zip; }; struct nest_friendslist { char name[10]; int age; struct address addr; char telephone[13]; } nest_friend; 在定义嵌套的结构类型时,必须先定义成员的结构类型,再定义主结构类型。
1.15.2结构变量的定义 三种定义结构变量的方式: 1.单独定义 先定义结构类型,再定义具有这种结构类型的变量 struct friends_list{ char name[10]; /* 姓名 */ int age; /* 年龄 */ char telephone[13]; /* 联系电话 */ }; struct friends_listfriend1, friend2;
结构变量的定义 2. 混合定义 在定义结构体类型的同时定义结构体变量 struct friends_list{ char name[10]; int age; char telephone[13]; } friend1, friend2; 3. 无类型名定义 在定义结构体变量时省略结构体名 struct{ char name[10]; int age; char telephone[13]; } friend1, friend2;
1.15.4结构变量的初始化 struct friends_listfriend1 = { "Zhang", 26, "0571-85171880 " } ; name age telephone ↓ ↓ ↓
结构变量的使用-整体赋值 具有相同类型的结构变量可以直接赋值。 将赋值符号右边结构变量的每一个成员的值都赋给了左边结构变量中相应的成员。 struct friends_list { char name[10]; int age; char telephone[13]; } friend1 = {Zhang",26, “0571-85271880”}, friend2; friend2 = friend1;
1.15.3访问结构变量的成员变量 1. 结构变量成员的引用 结构变量名 .结构成员名 friend1.age = 26; strcpy(friend1.name, "Zhang San"); nest_friend.addr.zip
pt ppt x y 2.结构体指针 • struct point{ int x;int y; }; struct point pt; /*定义结构体变量*/ struct point *ppt; /*定义结构体指针*/ ppt = &pt; • 怎样通过pt访问pt的成员? • pt.x = 0; /*成员运算符*/ • 怎样通过ppt访问pt的成员? • (*ppt).x = 0; • ppt->x = 0; /*指向运算符*/ • 第二种更常用
1.15.5结构体数组 初始化 struct STUDENT { int studentID; char studentName[10]; char studentSex[4]; struct date timeOfEnter; int scoreComputer; int scoreEnglish; int scoreMath; int scoreMusic; }; struct STUDENT stu[30] = {{1,"令狐冲","男",{1999,12,20},90,83,72,82}, {2,"林平之","男",{1999,07,06},78,92,88,78}, {3,"岳灵珊","女",{1999,07,06},89,72,98,66}, {4,"任莹莹","女",{1999,07,06},78,95,87,90} };
1 … 2 … 3 … 4 … 结构体数组的指针 • struct STUDENT *pt; • pt = stu; pt stu[0] pt++ stu[1] stu[2] stu[3]
入学时间 计算机 原理 性别 姓名 学号 英语 数学 音乐 年 月 日 例:利用指向结构体数组的指针计算学生各科的平均成绩 for (pt=stu; pt<stu+30; pt++) { sum[0] = sum[0] + pt->scoreComputer; sum[1] = sum[1] + pt->scoreEnglish; sum[2] = sum[2] + pt->scoreMath; sum[3] = sum[3] + pt->scoreMusic; } for (i=0; i<4; i++) { average[i] = sum[i]/4; printf("%20s : %4.2f\n", name[i], *(average+i)); }
例8.2 main() { struct STUDENT *pt; float sum[4] = {0.0},average[4] = {0.0}; int i; char *name[]={"score of Computer","score of English", "score of Math","score of Music"}; pt = stu; /*pt指向结构体数组的第一个元素*/ for (pt=stu; pt<stu+30; pt++) { sum[0] = sum[0] + pt->scoreComputer; sum[1] = sum[1] + pt->scoreEnglish; sum[2] = sum[2] + pt->scoreMath; sum[3] = sum[3] + pt->scoreMusic; } for (i=0; i<4; i++) { average[i] = sum[i]/4; printf("%20s : %4.2f\n", name[i], *(average+i)); } }
结构体与函数 • 向函数传递结构体的单个成员 • 单向值传递,函数内对结构内容的修改不影响原结构 • 向函数传递结构体的完整结构 • 单向值传递,函数内对结构内容的修改不影响原结构,开销大 • 向函数传递结构体的首地址 • 用结构体数组或者结构体指针做函数参数 • 除提高效率外,还可以修改结构体指针所指向的结构体的内容
思考 • 下面的结构什么意思? struct temp{int data;struct temp pt;} • TC下的错误提示: • Undefined structure ‘temp’ • Structure size too large • VC下的错误提示: • ‘pt’ uses undefined struct ‘temp’ • 下面的的呢? struct temp{int data;struct temp *pt;}
data next data next data next data NULL head 图 链表原理图 动态数据结构 • 结构体声明时不能包含自我,但可以包含指向本结构体类型的指针变量 • 链表(Linked table) struct Link {int data;struct Link *next; }
1000H 共用体,或称为联合(Union) • union number{short x;char ch; float y;}; • 基本上和struct一样 • x、ch和y处于同样的地址 • sizeof(union xxx)取决于占空间最多的那个成员变量
共用体的特点 • 同一内存单元在每一瞬时只能存放其中一种类型的成员;并非同时都起作用 • 起作用的成员是最后一次存放的成员 • 不能作为函数参数
共用体的应用 家庭状况 struct person { char name[20]; char sex; int age; union { int single; struct { char spouseName[20]; int child; }married; }marital; int marryFlag; }; 性别 sex 年龄 age 姓名name 未婚 已婚 婚姻状况 标记 配 偶 子 女
9901 Wang 80 9902 Li 90 9905 Qian 80 NULL head main Create_Stu_Doc InsertDoc DeleteDoc Print_Stu_Doc InsertDoc 学生信息管理链表程序解析 例 建立一个学生成绩信息(包括学号、姓名、成绩)的单向链表,学生记录按学号由小到大顺序排列,要求实现对成绩信息的插入、修改、删除和遍历操作。
例 数据定义与函数声明 struct stud_node{ /*链表结点类型*/ int num; char name[20]; int score; struct stud_node *next; }; struct stud_node * Create_Stu_Doc(); /* 新建链表 */ struct stud_node * InsertDoc(struct stud_node * head, struct stud_node *stud); /* 插入 */ struct stud_node * DeleteDoc(struct stud_node * head, int num); /* 删除 */ void Print_Stu_Doc(struct stud_node * head); /* 遍历 */
9901 Wang 80 9902 Li 90 9903 Qian 80 NULL head 链表的概念 • 一种动态存储分布的数据结构 • 若干个同一结构类型的“结点”依次串接而成 • 单向链表、双向链表 头指针 尾结点 结点
9901 Wang 80 9902 Li 90 9905 Qian 80 NULL head 链表的概念-结点定义 struct stud_node{ int num; char name[20]; int score; struct stud_node *next; }; 结构的递归定义
链表的概念-与数组比较 • 数组 • 事先定义固定长度的数组 • 在数组元素个数不确定时,可能会发生浪费内存空间的情况 • 链表 • 动态存储分配的数据结构 • 根据需要动态开辟内存空间,比较方便地插入新元素(结点) • 使用链表可以节省内存,提高操作效率
动态存储分配函数malloc() void *malloc(unsigned size) 在内存的动态存储区中分配一连续空间,其长度为size • 若申请成功,则返回一个指向所分配内存空间的起始地址的指针 • 若申请不成功,则返回NULL(值为0) • 返回值类型:(void *) • 通用指针的一个重要用途 • 将malloc的返回值转换到特定指针类型,赋给一个指针
p malloc()示例 int *ip = (int *) malloc( sizeof(int) ) struct student * p; p = (struct student *) malloc(sizeof(struct student)); • 调用malloc时,用 sizeof计算存储块大小 • 虽然存储块是动态分配的,但它的大小在分配后也是确定的,不要越界使用。
p 动态存储释放函数free 当某个动态分配的存储块不再用时,要及时将它释放 void free(void *ptr) 释放由动态存储分配函数申请到的整块内存空间,ptr为指向要释放空间的首地址。 free(ip); free(p);
单向链表的常用操作 1. 链表的建立 2. 链表的遍历 3. 插入结点 4. 删除结点
num name score next p 1.链表的建立 struct stud_node *head, *tail, *p; head = tail = NULL; size = sizeof(struct stud_node); p = (struct stud_node *) malloc(size); head p tail tail
程序段 head = tail = NULL; scanf("%d%s%d", &num,name, &score); while(num != 0){ p = (struct stud_node *) malloc(size); p->num = num; strcpy(p->name, name); p->score = score; p->next = NULL; if(head == NULL) head = p; else tail->next = p; tail = p; scanf("%d%s%d", &num, name, &score); }
9901 Wang 80 9902 Li 90 9905 Qian 80 NULL head ptr ptr 2. 链表的遍历 ptr->num ptr->score ptr=ptr->next for(ptr = head; ptr != NULL; ptr = ptr -> next) printf("%ld, %d", ptr -> num, ptr -> score);
链表的遍历-函数 void Print_Stu_Doc(struct stud_node * head) { struct stud_node * ptr; if(head == NULL){ printf("\nNo Records\n"); return; } printf("\nThe Students' Records Are: \n"); printf(" Num Name Score\n"); for(ptr = head; ptr!=NULL; ptr = ptr->next) printf("%8d %20s %6d \n", ptr->num, ptr->name, ptr->score); }
head ptr s 3. 插入结点 s->next = ptr->next ptr->next = s 先连后断
head ptr2 ptr2 ptr1 ptr1 head 4. 删除结点 ptr2=ptr1->next ptr1->next=ptr2->next free(ptr2) 先接后删