560 likes | 767 Views
C 语言程序设计. 第 8 章 结构体与共用体. 本章内容. 结构体(结构 structure )类型 共用体(联合 union )类型 结构体变量、结构体数组、结构体指针 向函数传递结构体 用结构体指针实现动态数据结构 链表的概念及操作原理. 从基本数据类型、复合数据类型到抽象数据类型. 二进制数 —— 类型本不存在 内存里存的内容,你认为它是什么,它就是什么 在早期的机器指令及汇编语言中,数据对象均用二进制数表示,没有类型的概念 一般的 CPU 只支持两种类型 整数、浮点数. 从基本数据类型、复合数据类型到抽象数据类型. 在高级语言引入了基本数据类型
E N D
C语言程序设计 第8章 结构体与共用体
本章内容 • 结构体(结构structure)类型 • 共用体(联合union)类型 • 结构体变量、结构体数组、结构体指针 • 向函数传递结构体 • 用结构体指针实现动态数据结构 • 链表的概念及操作原理
从基本数据类型、复合数据类型到抽象数据类型从基本数据类型、复合数据类型到抽象数据类型 二进制数——类型本不存在 内存里存的内容,你认为它是什么,它就是什么 在早期的机器指令及汇编语言中,数据对象均用二进制数表示,没有类型的概念 一般的CPU只支持两种类型 整数、浮点数
从基本数据类型、复合数据类型到抽象数据类型从基本数据类型、复合数据类型到抽象数据类型 在高级语言引入了基本数据类型 整型、浮点型、字符型等 不同语言会定义不同的基本类型 基本数据类型并不能方便地解决所有问题 有些语言(如PL/1)中试图规定较多的类型,如数组、树、栈等,但实践证明不是个好办法
从基本数据类型、复合数据类型到抽象数据类型从基本数据类型、复合数据类型到抽象数据类型 • 用户自己构造数据类型-复合数据类型 • 由基本数据类型迭代派生而来,表示复杂的数据对象 • 典型的代表就是“结构体” • 数组和指针
抽象数据类型(Abstract Data Type,简称ADT) 在复合数据类型基础上增加了对数据的操作 抽象数据类型进而进化为“类(Class)” 这是一个跨时代的进步 Class是Object-Oriented的一个重要概念 从基本数据类型、复合数据类型到抽象数据类型
思考一个问题 • 在程序里表示一个人(姓名、年龄、性别…),怎么表示? • 想表示多个人呢? • 如何用计算机程序实现下述表格的管理? 表8-1 某学校学生成绩管理表
数组的解决方法 int studentId[30]; /* 最多可以管理30个学生, 每个学生的学号用数组的下标表示*/ char studentName[30][10]; char studentSex[30][2]; int timeOfEnter[30]; /*入学时间用int表示*/ int scoreComputer[30];/*计算机原理课的成绩*/ int scoreEnglish[30]; /*英语课的成绩*/ int scoreMath[30]; /*数学课的成绩*/ int scoreMusic[30]; /*音乐课的成绩*/
数组的解决方法 int studentId[30] = {1,2,3,4,5,6}; char studentName[30][10]={{"令狐冲"},{"林平之"}, {"岳灵珊"},{"任莹莹"}}; char studentSex[30][2]={{"男"},{"男"},{"女"},{"女"}}; int timeOfEnter[30] = {1999,1999,1999,1999}; int scoreComputer[30] = {90,78,89,78}; int scoreEnglish[30] = {83,92,72,95}; int scoreMath[30] = {72,88,98,87}; int scoreMusic[30] = {82,78,66,90};
令狐冲 男 1999 1 1999 林平之 男 2 1999 岳灵珊 女 3 1999 任莹莹 女 4 …… …… …… …… 90 83 72 82 78 92 88 78 89 72 98 66 78 95 87 90 …… …… …… …… 数组的解决方法 • 数据的内存管理方式
令狐冲 男 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 希望的内存分配图
结构体的解决方法 • struct STUDENT{int studentID; /*每个学生的序号*/ char studentName[10];/*每个学生的姓名*/ char studentSex[4]; /*每个学生的性别*/ int timeOfEnter; /*每个学生的入学时间*/ int scoreComputer; /*每个学生的计算机原理成绩*/ int scoreEnglish; /*每个学生的英语成绩*/ int scoreMath; /*每个学生的数学成绩*/ int scoreMusic; /*每个学生的音乐成绩*/ }; • structSTUDENT是一个类型 • structSTUDENTstudents[4]; • students[0].studentIDstudents[0].scoreComputer • 它们都是变量,一般称为结构的成员变量
用户自定义的数据类型 • 结构体: • 把关系紧密、且逻辑相关的多种不同类型的变量,组织到统一的名字之下 • 占用相邻的一段内存单元 • 共用体: • 把情形互斥、但逻辑相关的多种不同类型的变量,组织到统一的名字之下 • 占用同一段内存单元,每一时刻只有一个数据起作用
age num name sex score addr 10010 LiFun M 18 87.5 Beijing Don’t forget the semicolon!! 结构体类型的定义 构成结构体的变量 称结构体成员(member) 也称域(filed) struct 结构体名 { 类型关键字成员名1; 类型关键字 成员名2; …... 类型关键字 成员名n; }; struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }; 形成一个样板 用于生成结构体变量
age num name sex score addr 10010 LiFun M 18 87.5 Beijing 结构体类型的定义 struct 结构体名 { 类型关键字成员名1; 类型关键字 成员名2; …... 类型关键字 成员名n; }; struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }; 只定义了数据的形式,即声明了一种复杂的数据类型,并未生成任何变量。
结构体变量的定义 • 先定义结构体类型再定义变量名 struct studentstudent1,student2; • 在定义类型的同时定义变量 struct { int num; char name[20]; char sex; int age; float score; char addr[30]; } student1,student2; struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; } student1,student2; • 直接定义结构体变量(不出现结构体名)
定义自己的类型名 structstudentstudent1,student2;/*It works*/ studentstudent1,student2;/*Can this work?*/ typedefstructstudentSTUD ; STUDstudent1,student2;/*It works!*/ struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }; typedef为一种已存在的类型定义一个新名字 STUD与structstudent类型是同义词
struct类型的特点 • 是一个一个普通的类型 • 可以定义该类型的变量、数组、指针…… • 可以做函数的参数类型和返回值类型 • 它的成员可以是任意类型 • 基本类型、数组、指针、结构体、共用体…… • struct类型的变量 • 两个结构体变量之间可以相互赋值 • 所以做为函数的参数时,是传值调用 • 可以取地址& • 不可直接参与算术和比较运算 • 面向对象和数据库是struct思想的发展
结构体的内存占用 • double 占用内存字节数 = 8 • struct 类型用内存字节数 = ? • 是所有成员变量的内存总和吗? struct number {short i;char ch; float f; }; • 用运算符sizeof获得结构体大小 sizeof(变量或表达式) sizeof(类型) printf("%d\n", sizeof(struct number)); Why? 8
i ch f i ch f 结构体的内存占用 非所有成员变量的内存总和 8个字节 事实上所有数据类型在内存中都是从偶数地址开始存放的 且结构所占的实际空间一般是按照机器字长对齐的 不同的编译器、平台,对齐方式会有变化 结构体变量的成员的存储对齐规则是与机器相关的 具有特定数据类型的数据项的大小也是与机器相关的 所以一个结构体在内存中的存储格式也是与机器相关的
入学时间 计算机 原理 性别 姓名 学号 英语 数学 音乐 年 月 日 结构体数组 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]; struct date { int year; int month; int day; }; 结构体定义 可以嵌套
结构体数组 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,08,26},90,83,72,82}, {2,"林平之","男",{1999,08,26},78,92,88,78}, {3,"岳灵珊","女",{1999,08,26},89,72,98,66}, {4,"任莹莹","女",{1999,08,26},78,95,87,90} };
男 男 女 女 林平之 令狐冲 任莹莹 岳灵珊 1 3 4 2 90 78 78 89 72 92 95 80 72 98 88 87 82 66 78 90 1999 1999 1999 1999 8 8 8 8 26 26 26 26 建立了数据库中的多条记录,每条对应一个学生信息 初始化 struct STUDENT stu[30] = {{1,"令狐冲","男",{1999,08,26},90,83,72,82}, {2,"林平之","男",{1999,08,26},78,92,88,78}, {3,"岳灵珊","女",{1999,08,26},89,72,98,66}, {4,"任莹莹","女",{1999,08,26},78,95,87,90} };
成员1 成员3 成员2 成员4 成员5 成员6 成员7 成员8 结构体变量的指针 stu struct STUDENT { int studentID; char studentName[10]; char studentSex[4]; struct date timeOfEnter; int scoreComputer; int scoreEnglish; int scoreMath; int scoreMusic; }; structSTUDENTstu; /*定义结构体变量*/ structSTUDENT*pStu; /*定义结构体指针*/ pStu = &stu; pStu
成员1 成员3 成员2 成员4 成员5 成员6 成员7 成员8 如何访问结构体的成员 stu struct STUDENT { int studentID; char studentName[10]; char studentSex[4]; struct date timeOfEnter; int scoreComputer; int scoreEnglish; int scoreMath; int scoreMusic; }; structSTUDENTstu; /*定义结构体变量*/ structSTUDENT*pStu; /*定义结构体指针*/ pStu = &stu; pStu • 通过stu和成员运算符访问结构体成员 • stu.studentID = 1; • 通过pStu和指向运算符访问结构体成员 • (*pStu).studentID = 1; • pStu->studentID = 1;
如何访问嵌套的结构体的成员 struct STUDENT { int studentID; char studentName[10]; char studentSex[4]; struct date timeOfEnter; int scoreComputer; int scoreEnglish; int scoreMath; int scoreMusic; }; structSTUDENTstu; /*定义结构体变量*/ structSTUDENT*pStu; /*定义结构体指针*/ pStu = &stu; struct date { int year; int month; int day; }; stu.timeOfEnter.year = 1999; (*pStu).timeOfEnter.year = 1999; pStu->timeOfEnter.year = 1999;
struct point{int x;int y;}; struct rect{struct point pt1;struct point pt2;}; struct rect rt; struct rect *rp = &rt; 下面表达式哪些合法? rt.pt1.x (*rp).pt1.x rp->pt1.x rt->pt1.x 上面合法的表达式都是等价的吗? 思考题
stu[0] stu[2] stu[1] stu[3] 结构体数组的指针 stu[4] struct STUDENT { int studentID; char studentName[10]; char studentSex[4]; struct date timeOfEnter; int scoreComputer; int scoreEnglish; int scoreMath; int scoreMusic; }; struct STUDENT *pStu; /*定义结构体指针*/ pStu = stu; /*相当于pStu = &stu[0];*/ pStu struct STUDENT stu[4] = {{1,"令狐冲","男",{1999,08,26},90,83,72,82}, {2,"林平之","男",{1999,08,26},78,92,88,78}, {3,"岳灵珊","女",{1999,08,26},89,72,98,66}, {4,"任莹莹","女",{1999,08,26},78,95,87,90} };
stu[0] stu[2] stu[1] stu[3] 如何访问结构体数组元素的成员 stu[4] struct STUDENT { int studentID; char studentName[10]; char studentSex[4]; struct date timeOfEnter; int scoreComputer; int scoreEnglish; int scoreMath; int scoreMusic; }; struct STUDENT *pStu; /*定义结构体指针*/ pStu = stu; /*相当于pStu = &stu[0];*/ pStu • 使用pStu++,使pStu指向stu[i] • pStu -> studentID • 等价于stu[i]. studentID
向函数传递结构体 • 向函数传递结构体的单个成员 • 复制单个成员的内容 • 函数内对结构内容的修改不影响原结构 • 向函数传递结构体的完整结构? • 向函数传递结构体的首地址?
struct date { int year; int month; int day; }; void func(struct datep) { p.year = 2000; p.month = 5; p.day = 22; } 结构体变量做函数参数 1999,4,23 1999,4,23 main() { struct date d; d.year = 1999; d.month = 4; d.day = 23; printf(“%d,%d,%d\n”, d.year, d.month, d.day); func(d); printf(“%d,%d,%d\n”, d.year, d.month, d.day); }
struct date { int year; int month; int day; }; void func(struct date*p) { p->year = 2000; p->month = 5; p->day = 22; } 结构体指针做函数参数 1999,4,23 2005,5,22 指针做函数形参 实参必须为地址值 main() { struct date d; d.year = 1999; d.month = 4; d.day = 23; printf(“%d,%d,%d\n”, d.year, d.month, d.day); func(&d); printf(“%d,%d,%d\n”, d.year, d.month, d.day); }
struct date { int year; int month; int day; }; struct date func(struct datep) { p.year = 2000; p.month = 5; p.day = 22; } 结构体变量做函数返回值 存在一个错误 main() { struct date d; d.year = 1999; d.month = 4; d.day = 23; printf(“%d,%d,%d\n”, d.year, d.month, d.day); d = func(d); printf(“%d,%d,%d\n”, d.year, d.month, d.day); }
struct date { int year; int month; int day; }; struct date func(struct datep) { p.year = 2000; p.month = 5; p.day = 22; return p; } 结构体变量做函数返回值 1999,4,23 2005,5,22 main() { struct date d; d.year = 1999; d.month = 4; d.day = 23; printf(“%d,%d,%d\n”, d.year, d.month, d.day); d = func(d); printf(“%d,%d,%d\n”, d.year, d.month, d.day); }
向函数传递结构体 • 向函数传递结构体的完整结构 • 复制整个结构体成员的内容,多个值 • 函数内对结构内容的修改不影响原结构 • 内容传递更直观,但开销大 • 向函数传递结构体的首地址 • 用结构体数组/结构体指针做函数参数 • 仅复制结构体的首地址,一个值 • 修改结构体指针所指向的结构体的内容 • 指针传递效率高
例8.2:利用指向结构体数组的指针计算学生各科的平均成绩例8.2:利用指向结构体数组的指针计算学生各科的平均成绩 入学时间 计算机 原理 性别 姓名 学号 英语 数学 音乐 年 月 日 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;}; • CB下的错误提示: • field 'pt' has incomplete type • TC下的错误提示: • Undefined structure ‘temp’ • Structure size too large • VC下的错误提示: • 'pt' uses undefined struct ‘temp’ • 下面的结构是什么意思呢? struct temp{int data;struct temp *pt;}; 可包含指向本结构体类型的指针变量
数组:线性表的顺序表示方式 D E C D E 元素 A B C D E 序号 0 1 2 3 4 • 优点:便于快速、随机存取线性表中任一元素 • 缺点:插入和删除操作需要移动大量元素 元素 A B C X 下标 0 1 2 3 4 5 元素 A B 下标 0 1 2 3 4
数组:线性表的顺序表示方式 元素 A B C D E 序号 0 1 2 3 4 • 缺点: • 属于静态内存分配,程序一旦运行长度不能改变。若想改变,只能修改程序。一旦确定,会带来两个问题: • 1)不能超过数组元素最大长度的限制,否则溢出。 • 2)数组元素低于所设定的最大长度,将造成系统资源的浪费。
思考 • 能否有一个办法,保证系统资源的最合理运用? • 当我们需要添加一个元素时,程序会自动添加; • 当我们需要减少一个元素时,程序会自动放弃该元素原来占有的内存。 • 动态数据结构 • 结构体 • 动态内存分配
data next data next data next data NULL head 链表的定义 • 链表(Linked table):线性表的链式存储结构 • 特点:用一组任意的存储单元存储线性表的数据;存储单元可以是连续的,也可是不连续的 struct Link {int data;struct Link *next; };
data next data next data next data NULL head 链表的定义 • 链表(Linked table):线性表的链式存储结构 • 为表示每个元素与后继元素的逻辑关系,除存储元素本身信息外,还要存储其直接后继信息 struct Link {int data;struct Link *next; }; 两部分信息组成一个结点
data next data next data next data NULL head 链表的定义 • 链表(Linked table):线性表的链式存储结构 • 为表示每个元素与后继元素的逻辑关系,除存储元素本身信息外,还要存储其直接后继信息 struct Link {int data;struct Link *next; }; 数据域:存储数据元素信息 指针域:存储直接后继的节点信息
data next data next data next data NULL head 链表的定义 • 链表(Linked table):线性表的链式存储结构 • n个节点链接成一个链表(因为只包含一个指针域,故又称线性链表或单向链表) struct Link {int data;struct Link *next; }; 数据域:存储数据元素信息 指针域:存储直接后继的节点信息
链表的典型实现 C语言代码 head data = A node data = B node data = C ∧ node struct Link {int data;struct Link *next; }; 链表的头指针:便于操作 空指针NULL表示链表结尾
链表的插入操作 在x后插入新节点 t = malloc(sizeof(struct Node)); t->next = x->next; x->next = t; t C A B x
链表的删除操作 删除节点 x -> next t = x->next; x->next = t->next; free(t); t A B x C
i ch f 0x0037b00 共用体,或称为联合(Union) printf("%d\n", sizeof(struct number)); struct number {short i;char ch; float f; }; 8个字节 printf("%d\n", sizeof(union number)); union number {short i;char ch; float f; }; ch f i 4个字节