1 / 56

第 8 章 结构体与共用体

C 语言程序设计. 第 8 章 结构体与共用体. 本章内容. 结构体(结构 structure )类型 共用体(联合 union )类型 结构体变量、结构体数组、结构体指针 向函数传递结构体 用结构体指针实现动态数据结构 链表的概念及操作原理. 从基本数据类型、复合数据类型到抽象数据类型. 二进制数 —— 类型本不存在 内存里存的内容,你认为它是什么,它就是什么 在早期的机器指令及汇编语言中,数据对象均用二进制数表示,没有类型的概念 一般的 CPU 只支持两种类型 整数、浮点数. 从基本数据类型、复合数据类型到抽象数据类型. 在高级语言引入了基本数据类型

emelda
Download Presentation

第 8 章 结构体与共用体

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. C语言程序设计 第8章 结构体与共用体

  2. 本章内容 • 结构体(结构structure)类型 • 共用体(联合union)类型 • 结构体变量、结构体数组、结构体指针 • 向函数传递结构体 • 用结构体指针实现动态数据结构 • 链表的概念及操作原理

  3. 从基本数据类型、复合数据类型到抽象数据类型从基本数据类型、复合数据类型到抽象数据类型 二进制数——类型本不存在 内存里存的内容,你认为它是什么,它就是什么 在早期的机器指令及汇编语言中,数据对象均用二进制数表示,没有类型的概念 一般的CPU只支持两种类型 整数、浮点数

  4. 从基本数据类型、复合数据类型到抽象数据类型从基本数据类型、复合数据类型到抽象数据类型 在高级语言引入了基本数据类型 整型、浮点型、字符型等 不同语言会定义不同的基本类型 基本数据类型并不能方便地解决所有问题 有些语言(如PL/1)中试图规定较多的类型,如数组、树、栈等,但实践证明不是个好办法

  5. 从基本数据类型、复合数据类型到抽象数据类型从基本数据类型、复合数据类型到抽象数据类型 • 用户自己构造数据类型-复合数据类型 • 由基本数据类型迭代派生而来,表示复杂的数据对象 • 典型的代表就是“结构体” • 数组和指针

  6. 抽象数据类型(Abstract Data Type,简称ADT) 在复合数据类型基础上增加了对数据的操作 抽象数据类型进而进化为“类(Class)” 这是一个跨时代的进步 Class是Object-Oriented的一个重要概念 从基本数据类型、复合数据类型到抽象数据类型

  7. 思考一个问题 • 在程序里表示一个人(姓名、年龄、性别…),怎么表示? • 想表示多个人呢? • 如何用计算机程序实现下述表格的管理? 表8-1 某学校学生成绩管理表

  8. 数组的解决方法 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]; /*音乐课的成绩*/

  9. 数组的解决方法 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};

  10. 令狐冲 男 1999 1 1999 林平之 男 2 1999 岳灵珊 女 3 1999 任莹莹 女 4 …… …… …… …… 90 83 72 82 78 92 88 78 89 72 98 66 78 95 87 90 …… …… …… …… 数组的解决方法 • 数据的内存管理方式

  11. 令狐冲 男 1999 1 1999 林平之 男 2 1999 岳灵珊 女 3 1999 任莹莹 女 4 …… …… …… …… 90 83 72 82 78 92 88 78 89 72 98 66 78 95 87 90 …… …… …… …… 数组的解决方法 • 数据的内存管理方式 分配内存不集中,寻址效率不高 对数组进行赋初值时,容易发生错位 结构显得比较零散,不容易管理

  12. 1 2 3 4 林平之 岳灵珊 任莹莹 令狐冲 男 女 女 男 1999 1999 1999 1999 78 89 78 90 92 72 95 83 88 98 87 72 78 66 90 82 希望的内存分配图

  13. 结构体的解决方法 • 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 • 它们都是变量,一般称为结构的成员变量

  14. 用户自定义的数据类型 • 结构体: • 把关系紧密、且逻辑相关的多种不同类型的变量,组织到统一的名字之下 • 占用相邻的一段内存单元 • 共用体: • 把情形互斥、但逻辑相关的多种不同类型的变量,组织到统一的名字之下 • 占用同一段内存单元,每一时刻只有一个数据起作用

  15. 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]; }; 形成一个样板 用于生成结构体变量

  16. 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]; }; 只定义了数据的形式,即声明了一种复杂的数据类型,并未生成任何变量。

  17. 结构体变量的定义 • 先定义结构体类型再定义变量名 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; • 直接定义结构体变量(不出现结构体名)

  18. 定义自己的类型名 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类型是同义词

  19. struct类型的特点 • 是一个一个普通的类型 • 可以定义该类型的变量、数组、指针…… • 可以做函数的参数类型和返回值类型 • 它的成员可以是任意类型 • 基本类型、数组、指针、结构体、共用体…… • struct类型的变量 • 两个结构体变量之间可以相互赋值 • 所以做为函数的参数时,是传值调用 • 可以取地址& • 不可直接参与算术和比较运算 • 面向对象和数据库是struct思想的发展

  20. 结构体的内存占用 • double 占用内存字节数 = 8 • struct 类型用内存字节数 = ? • 是所有成员变量的内存总和吗? struct number {short i;char ch; float f; }; • 用运算符sizeof获得结构体大小 sizeof(变量或表达式) sizeof(类型) printf("%d\n", sizeof(struct number)); Why? 8

  21. i ch f i ch f 结构体的内存占用 非所有成员变量的内存总和 8个字节 事实上所有数据类型在内存中都是从偶数地址开始存放的 且结构所占的实际空间一般是按照机器字长对齐的 不同的编译器、平台,对齐方式会有变化 结构体变量的成员的存储对齐规则是与机器相关的 具有特定数据类型的数据项的大小也是与机器相关的 所以一个结构体在内存中的存储格式也是与机器相关的

  22. 入学时间 计算机 原理 性别 姓名 学号 英语 数学 音乐 年 月 日 结构体数组 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; }; 结构体定义 可以嵌套

  23. 结构体数组 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} };

  24. 男 女 女 林平之 令狐冲 任莹莹 岳灵珊 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} };

  25. 成员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

  26. 成员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;

  27. 如何访问嵌套的结构体的成员 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;

  28. 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 上面合法的表达式都是等价的吗? 思考题

  29. 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} };

  30. 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

  31. 向函数传递结构体 • 向函数传递结构体的单个成员 • 复制单个成员的内容 • 函数内对结构内容的修改不影响原结构 • 向函数传递结构体的完整结构? • 向函数传递结构体的首地址?

  32. 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); }

  33. 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); }

  34. 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); }

  35. 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); }

  36. 向函数传递结构体 • 向函数传递结构体的完整结构 • 复制整个结构体成员的内容,多个值 • 函数内对结构内容的修改不影响原结构 • 内容传递更直观,但开销大 • 向函数传递结构体的首地址 • 用结构体数组/结构体指针做函数参数 • 仅复制结构体的首地址,一个值 • 修改结构体指针所指向的结构体的内容 • 指针传递效率高

  37. 例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)); }

  38. 例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)); } }

  39. 结构体声明时不能包含本结构体类型成员 系统将无法为这样的结构体类型分配内存 思考 • 下面的结构是什么意思? 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;}; 可包含指向本结构体类型的指针变量

  40. 数组:线性表的顺序表示方式 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

  41. 数组:线性表的顺序表示方式 元素 A B C D E 序号 0 1 2 3 4 • 缺点: • 属于静态内存分配,程序一旦运行长度不能改变。若想改变,只能修改程序。一旦确定,会带来两个问题: • 1)不能超过数组元素最大长度的限制,否则溢出。 • 2)数组元素低于所设定的最大长度,将造成系统资源的浪费。

  42. 思考 • 能否有一个办法,保证系统资源的最合理运用? • 当我们需要添加一个元素时,程序会自动添加; • 当我们需要减少一个元素时,程序会自动放弃该元素原来占有的内存。 • 动态数据结构 • 结构体 • 动态内存分配

  43. data next data next data next data NULL head 链表的定义 • 链表(Linked table):线性表的链式存储结构 • 特点:用一组任意的存储单元存储线性表的数据;存储单元可以是连续的,也可是不连续的 struct Link {int data;struct Link *next; };

  44. data next data next data next data NULL head 链表的定义 • 链表(Linked table):线性表的链式存储结构 • 为表示每个元素与后继元素的逻辑关系,除存储元素本身信息外,还要存储其直接后继信息 struct Link {int data;struct Link *next; }; 两部分信息组成一个结点

  45. data next data next data next data NULL head 链表的定义 • 链表(Linked table):线性表的链式存储结构 • 为表示每个元素与后继元素的逻辑关系,除存储元素本身信息外,还要存储其直接后继信息 struct Link {int data;struct Link *next; }; 数据域:存储数据元素信息 指针域:存储直接后继的节点信息

  46. data next data next data next data NULL head 链表的定义 • 链表(Linked table):线性表的链式存储结构 • n个节点链接成一个链表(因为只包含一个指针域,故又称线性链表或单向链表) struct Link {int data;struct Link *next; }; 数据域:存储数据元素信息 指针域:存储直接后继的节点信息

  47. 链表的典型实现 C语言代码 head data = A node data = B node data = C ∧ node struct Link {int data;struct Link *next; }; 链表的头指针:便于操作 空指针NULL表示链表结尾

  48. 链表的插入操作 在x后插入新节点 t = malloc(sizeof(struct Node)); t->next = x->next; x->next = t; t C A B x

  49. 链表的删除操作 删除节点 x -> next t = x->next; x->next = t->next; free(t); t A B x C

  50. 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个字节

More Related