第七章 结构和模块化设计 7.1 结构

49
第第第 第第第第第第第第 7.1 第第 第第第第第第第第第 第第第第第第第第第第第第第第第第第第第 第第 第第第第第 第第第第第第 第第 第第第 第第第 第第第 第第 :、、、 第第第第 C 第第第第第第 Pascal 第第第第第第 C++ Java 第第第第第

Upload: orlando-conley

Post on 15-Mar-2016

187 views

Category:

Documents


3 download

DESCRIPTION

第七章 结构和模块化设计 7.1 结构. 复合数据对象的需求 描述由不同类型的数据元素组成的数据对象 例如: 教学管理系统中的学生数据 包括:姓名、性别、学号、班级 语言支持 C 语言中的结构 Pascal 语言中的记录 C++ 、 Java 语言中的类. 语法结构 struct 结构名 { 类型 成员变量; 。。。 }; /* 学生信息的结构 * / struct Student { char name[32]; char sex; int no; char class[8]; };. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第七章  结构和模块化设计 7.1   结构

第七章 结构和模块化设计7.1 结构

复合数据对象的需求 描述由不同类型的数据元素组成的数据对象

例如: 教学管理系统中的学生数据 包括:姓名、性别、学号、班级

语言支持 C 语言中的结构 Pascal 语言中的记录 C++ 、 Java 语言中的类

Page 2: 第七章  结构和模块化设计 7.1   结构

结构定义和变量说明 语法结构struct 结构名 { 类型 成员变量;。。。};/* 学生信息的结构 */struct Student {

char name[32];char sex;int no;char class[8];

};

结构变量说明struct Student chen;

或直接说明struct {

char name[32];char sex;int no;char class[8];

} chen;

Page 3: 第七章  结构和模块化设计 7.1   结构

结构变量及相关运算 结构变量占用的内存空间

大于等于各个元素占用内存空间之和 结构变量的运算

初始化如: struct Student chen= { “chen”, ‘F’, 24, “9707” }; 赋值 函数参数

结构分量的运算 引用: chen.name chen.sex 运算取决于分量的类型

Page 4: 第七章  结构和模块化设计 7.1   结构

例 7-1 :日期的验证#include <stdio.h>struct date {

int year; /* 年 */unsigned char month; /* 月 */unsigned char day; /* 日 */

};main( ) {

int days[ ] = {0,31,28,31,30,31,30,31,31,30,31,30,31};struct date x;do {

scanf( “%d%d%d”, &x.year, &x.month, &x.day );} while( x.day <= 0 || x.day > days[x.month]

|| x.month <= 0 || x.month > 12 ); /* 出错要求重新输入 */

printf( “It is correct date.\n” );}

Page 5: 第七章  结构和模块化设计 7.1   结构

程序读解 结构的使用

分量使用:各种运算、取地址、作为参数; 分量类型:指针、其他结构(不允许递归定义)

unsigned char 无符号字符( 1个字节: 0-255 ) %d 指示读整数,存到该字节

数组 days 的使用 避免 switch 语句的多分支 执行速度高于 switch

Page 6: 第七章  结构和模块化设计 7.1   结构

例 7-2 :洗牌和发牌模拟 任务:

考虑扑克牌的 4 个花色和 52 张,完成洗牌过程,通过输出完成发牌过程 基本思路

设置数据对象保存 52 张牌、 4 个花色和牌名信息 产生随机数来控制牌的交换,模拟洗牌过程 输出所有牌,分为 4 组,模拟发牌过程

Page 7: 第七章  结构和模块化设计 7.1   结构

算法设计 数据对象设计

52 张牌 cards[52]

4 个花色 suit[4]13 个牌号 face[13]

算法1. 初始化 52 张牌2. 洗牌过程3. 发牌过程

初始化依次设置每张牌的花色和牌号

洗牌过程依次处理每张牌 ( 第 i 张 )产生一个随机数 (0-51) j将第 j 张和第 i 张牌交换

Page 8: 第七章  结构和模块化设计 7.1   结构

程序结构设计 数据结构

采用 struct 描述扑克牌的信息(花色、牌号) 采用整数数组表示牌号 1, 2, 3, … 13 采用字符数组表示花色 H, D, C, S

程序结构 设置 3 个函数 fillDeck, shuffle, deal 分别负责初始化、洗牌和发牌的完成

Page 9: 第七章  结构和模块化设计 7.1   结构

程序实现 (1/3)#include <stdio.h>

#include <stdlib.h>#include <time.h>struct card {

int face; char suit;};main( ){

struct card deck[52];int face[ ] = {1,2,3,4,5,6,7,8,9,10,11,12,13};char suit[ ] = { ‘H’, ‘D’, ‘C’, ‘S’ };srand( time(NULL) );fillDesk( deck, face, suit );shuffle( deck );deal( deck );

}

通用函数头文件时间函数头文件

数组初始化

获得当前时间

初始化随机数函数

Page 10: 第七章  结构和模块化设计 7.1   结构

程序实现 (2/3)void fillDeck( struct card deck[ ], int face[ ], char

suit[ ] ){

int i; /* 初始化:顺序排列 52 张牌 */for( i=0; i<52; i++ ) {deck[ i ].face = face[ i%13 ];deck[ i ].suit = suit[ i/13 ];}

} 结构分量的引用

Page 11: 第七章  结构和模块化设计 7.1   结构

程序实现 (3/3)void shuffle(

struct card deck[ ] ){

int i, j; /* 洗牌过程 */struct card temp;for( i=0; i<52; i++ ) {j = rand( ) %52;temp = deck[ i ];deck[ i ] = deck [ j ];deck[ j ] = temp;

}}

void deal( struct card deck[ ] ){ int i; /* 发牌过程 */ for( i=0; i<52; i++ )

printf(“%d of %c%c”, deck[ i ].face, deck[ i ].suit, (i+1)%4? ‘\t’: ‘\n’ );

}

条件表达式 制表符随机数生成

Page 12: 第七章  结构和模块化设计 7.1   结构

几点程序说明 结构数组

元素是一个完整的结构 结构变量的赋值 temp = desk[ i ]

整个结构的复制(时间开销大) 数组初始化

可利用初值个数设置数组大小 随机数的产生

int rand( ) 返回随机数 void srand( unsigned seed ) 初始化随机数生成 time( ) 获得当前时间(单位:秒) 需要通用函数和时间函数头文件 stdlib.h, time.h

Page 13: 第七章  结构和模块化设计 7.1   结构

虚拟机的概念 抽象的计算机

接受一组指令,按照给定的指令序列执行 虚拟机概念在程序设计中的应用

使得一个程序模块,按照类似的方式工作: 以一组函数实现虚拟机的指令系统 以函数调用表示指令的执行

例如: 上例中,扑克牌数组以及相关的 3 个函数组成一个虚拟机; 3 个函数实现了该虚拟机的 3 个指令。 使用者( main 函数)仅负责向虚拟机发送指令(函数调用),不必关系其内部实现细节

Page 14: 第七章  结构和模块化设计 7.1   结构

抽象方法与信息隐蔽 抽象方法的运用

通过 3 个函数的设置( fillDeck, shuffle, deal ),完全隐蔽了扑克牌相关处理的内部计算逻辑; 虚拟机使用者的视点

仅了解 3 个函数的使用方法,好象它们是系统提供的标准函数 对软件工程的支持

虚拟机的使用者和实现者可以是不同的开发组 分别实现大型软件的不同组成部分 软件模块接口就是一组函数原型及其说明

Page 15: 第七章  结构和模块化设计 7.1   结构

7.2 模块化程序设计 软件模块

数据对象及其相关处理算法 程序模块:数据结构及其相关函数

例 7-1 中的模块 主控模块: main 函数 扑克牌模块:数组 deck 以及 3 个函数

信息隐蔽 模块的使用者无须连接数据对象的细节如: main 函数编制时,无须了解数组 deck 的详细定义、数据内容和结构组织。

Page 16: 第七章  结构和模块化设计 7.1   结构

例 7-3 :简易学生管理系统 任务:

分别输入学生的户籍信息和学籍信息,打印出学生基本信息表(假设学生人数 <250人) 户籍信息:姓名、身份证号码、出生年月日、住址; 学籍信息:学号、身份证号码、所属学院、专业、班级 学生基本信息表:学号、姓名、年龄、所属学院、班级;

数据对象 学生户籍数据、学生学籍数据 学生基本信息表可以直接输出(不保存)

Page 17: 第七章  结构和模块化设计 7.1   结构

算法设计1. 输入户籍数据

(每行输入一个学生的数据,空格分割各个项目)2. 输入学籍数据

(每行输入一个学生的数据,空格分割各个项目)3. 构造并输出学生基本信息表

(提取户籍和学籍数据,构造并输出学生信息表)

Page 18: 第七章  结构和模块化设计 7.1   结构

算法的逐步求精 1 、 2 步

仅涉及输入输出,忽略算法描述 3 构造学生基本信息表

3.1 依次从户籍数据中取出一个学生的信息 3.2 根据其身份证号码,找出该生的学籍信息 3.3 综合该生的户籍信息和学籍信息,构造基本信息记录,填入学生基本信息表 3.4 重复 3.1-3.3 的处理,直至处理完所有学生的数据

Page 19: 第七章  结构和模块化设计 7.1   结构

程序结构设计 数据对象

户籍数据、学籍数据、学生基本信息表 尽可能符合信息的原始结构、采用 struct 采用结构数组来保存数据

模块与函数的设计 围绕户籍数据,提供输入、依次提取的函数 围绕学籍数据,提供输入、查找的函数 为学生基本信息,提供构造并输出的函数 形成 3 个程序模块

Page 20: 第七章  结构和模块化设计 7.1   结构

程序结构

户籍数据模块InfoAddr数据接口InputAddr( … )输入数据

学籍数据模块InfoStudent学籍数据InputStudent( … )输入数据GetStudent( … )查找学生信息

基本信息表模块Output( … )基本信息表的输出

主控模块main( )

Page 21: 第七章  结构和模块化设计 7.1   结构

户籍处理模块的设计#include <stdio.h>#include <string.h>typedef struct { /* 户籍数据结构 */

char name[ 16 ]; /* 姓名 */long no; /* 身份证号 */struct {

int year, mon, day; /* 作为分量的结构 */} birthday; /* 生日 */char addr[ 128 ]; /* 地址 */

} InfoAddr;int InputAddr( InfoAddr info[ ] );

/* 输入数据到数组 info, 返回学生数量 */

Page 22: 第七章  结构和模块化设计 7.1   结构

学籍处理模块的设计typedef struct { /* 学籍数据结构 */

char student[ 20 ]; /* 学号 */long no; /* 身份证号 */char college[ 32 ]; /* 学院 */char class[ 10 ]; /* 班级 */

} InfoStudent;

int InputStudent( InfoStudent info[ ] );/* 输入数据到数组 info, 返回学生数量 */

InfoStudent *GetStudent( long no, InfoStudent info[ ],

int n );/* 获得身份证号为 no 的学生的学籍数据 , n 是学生数量 */

Page 23: 第七章  结构和模块化设计 7.1   结构

主控模块的设计void Output(int n, InfoAddr addr[ ], InfoStudent info[ ]);

/* 构造学生基本信息,按表格输出( n 是学生人数) */

main( ){

static InfoAddr addr[ 256 ];static InfoStudent stu[ 256 ];int num; /* 人数 */num = InputAddr( addr ); /* 输入户籍数据 */if( num != InputStudent( stu ) ) /* 输入学籍数据 */return;Output( num, addr, stu ); /* 输出基本信息表 */

}

Page 24: 第七章  结构和模块化设计 7.1   结构

设计说明 函数原型设计

考虑函数内部功能的实现中,需要用到的所有输入输出信息; 将输入信息作为参数;输出结果作为返回值,或通过参数返回; 确保信息处理的局部化

实现技术细节 对于复杂结构,利用结构,结构分量的结构 对于大的数据对象(结构数组),采用静态变量

Page 25: 第七章  结构和模块化设计 7.1   结构

学生基本信息表的构造和输出void Output(int n, InfoAddr addr[ ], InfoStudent info[ ]){

int i, age;InfoAddr *q; /* 结构指针 */InfoStudent *p;for( i=0; i<n; i++ ) {q = &addr[ i ]; /* 取户籍数据 */age = 2005 – q->birthday.year; /* 计算年龄 */p = GetStudent( q->no, info, n ); /* 取学籍数据 */printf( “%8ld %.16s %2d %.32s %.10s\n”, q->no, q->name, age, p->college, p->class );

}}

Page 26: 第七章  结构和模块化设计 7.1   结构

户籍信息的输入int InputAddr( InfoAddr info[ ] ){ /* 输入数据到数组 info, 返回学生数量 */

int n = 6;char buf[ 256 ];InfoAddr *p;for( p = info; n == 6; p++ ) {

gets(buf); /* 读入一行 */n = sscanf( buf, “%s%ld%d%d%d%.128s”,

p->name, &p->no, &p->birthday.year,&p->birthday.mon, &p->birthday.day,p->addr ); /* 读入到数组元素中 */

}return p – info - 1;

}

Page 27: 第七章  结构和模块化设计 7.1   结构

学籍数据的输入int InputStudent( InfoStudent info[ ] ){ /* 输入数据到数组 info, 返回学生数量 */

int n = 4;char buf[ 256 ];InfoStudent *p;for( p=info; n == 4; p++ ) {gets( buf ); /* 读入一行 */n = sscanf( buf, “%s%ld%s%s”,p->student, &p->no, p->college, p->class );} /* 无足够输入时终止 */return p – info - 1;

}

Page 28: 第七章  结构和模块化设计 7.1   结构

查找学籍数据的实现InfoStudent *GetStudent( long no,

InfoStudent info[ ], int n ){

/* 获得身份证号为 no 的学生的学籍数据 */int i;for( i=0; i<n; i++ ) {

if( info[ i ]->no == no )return &info[ i ];

}return NULL;

}

Page 29: 第七章  结构和模块化设计 7.1   结构

设计方法 功能分解

按照系统功能,逐步求精 使用函数进行功能抽象

模块化 数据抽象 围绕数据组织程序模块 数据结构 + 相关函数

异常处理 考虑各种错误的可能性 特别是数据输入中的各种错误

Page 30: 第七章  结构和模块化设计 7.1   结构

格式化输入输出的说明 转换说明符( scanf 、 printf )

格式: %m.nd 宽度 m :用于整数和实数的输入、字符串的输入 精度 n :用于小数部分的输出位数、字符串的输出宽度

返回值 scanf : 匹配的数据项个数 printf : 输出的字符个数

& 表示取变量的内存单元地址 数组名本身是内存单元地址,不用 &

Page 31: 第七章  结构和模块化设计 7.1   结构

其他输入输出 字符输入

int getchar( ) 从标准输入读入一字符,返回 ASCII 值gets( char buf[ ] ) 从标准输入读入一行字符,返回数组名或 0

来自字符数组的格式化输入和输出 sscanf( char buf[ ], char fmt[ ], … ) sprintf( char buf[ ], char fmt[ ], … )

Page 32: 第七章  结构和模块化设计 7.1   结构

各种数据类型的表示范围类型 范围 内存char -128, 127 1byteunsigned char 0,255 1byteshort -32768,32767 2byteunsigned short 0,65535 2bytelong -2147483648, 2147483647 4byteunsigned long 0,4294967295 4bytefloat -3.4E+38, 3.4E+38 4bytedouble -1.7E+308, 1.7E+308 8byte

Page 33: 第七章  结构和模块化设计 7.1   结构

模块化程序的文件组织 标准的组织方法

将所有结构定义、函数原型放在某个头文件中( *.h ) 将每个模块的函数定义和全局变量放在同一个源程序文件中( *.c ) 每个源程序文件中,添加 #include “ 头文件名” 建立工程文件,统一管理所有源程序文件

Page 34: 第七章  结构和模块化设计 7.1   结构

7.3 动态数据结构与链表 数组的应用条件:

必须预先确定上限 无法适应元素个数可变的应用需求

解决方案 采用动态数据结构 根据需求,在运行中申请存储空间

常用的动态数据结构 链表、树、图等

Page 35: 第七章  结构和模块化设计 7.1   结构

链表的概念 链表是利用指针将一组数据组成起来的数据结构:

指针提供了查找数据的手段

表头

数据 指针 数据 指针 数据 NULL

Page 36: 第七章  结构和模块化设计 7.1   结构

学籍信息链表struct Link { /* 链表节点结构 */InfoStudent data; /* 学籍信息 */struct Link *next; /* 指向下一节点 */};使用特征 构造时,逐个建立节点和连接关系 访问时,从表头逐个查找 增加或删除数据时,仅改变指针连接关系

Page 37: 第七章  结构和模块化设计 7.1   结构

例 7-4 :一个读入学籍信息构造链表的函数 函数原型

struct Link *InputStudent(void) 从 stdin 读数据、返回链表表头

数据对象 表头指针 p 当前节点指针 q 读入的学籍信息 x

读取一行学籍信息

读成功?

返回表头 添加到表头

表头置空

Y

N

Page 38: 第七章  结构和模块化设计 7.1   结构

程序实现

struct Link *InputStudent( void ) {char buf[256]; /* 输入数据构造链表,返回表头指针 */InfoStudent x;struct Link *p = NULL, *q;while( 1 ) {gets( buf ); /* 读取一行字符 */if( 4 != sscanf( buf, “%s%ld%s%s”,x.student, &x.no, x.college, x.class ) ) return p; /* 无足够数据则终止 */q = (struct Link *)malloc(sizeof(struct Link));if( NULL == q ) return p;q->data = x; q->next = p;p = q; /* 加在表头 */

}}

Page 39: 第七章  结构和模块化设计 7.1   结构

例 7-5 :学籍管理系统 提供学生信息输入,信息输出和指定信息删除的功能 功能分解:

命令输入(主控) 信息输入( InputStudent ) 信息输出( OutputStudent ) 指定信息删除

Page 40: 第七章  结构和模块化设计 7.1   结构

数据对象和主控逻辑 数据对象

一组学生信息(数量不定) 采用链表实现(易于维护) 表头 pHead 命令 c 学号 no

命令输入C

信息删除

信息输入

信息输出

C=1

C=2

C=3输入学号 n

Page 41: 第七章  结构和模块化设计 7.1   结构

函数抽象的运用 int GetCommand( void );

读取用户命令,返回 struct Link *InputStudent( void );

负责信息输入(输入一组学籍数据,组织成链表) void OutputStudent( struct Link *p );

负责信息输出(输出链表中的所有学籍数据) struct Link *RemoveStudent( struct Link *p, char *n );

负责信息删除 删除学号 n 指定的学生信息,返回修改后的链表

void DeleteLink( struct Link *p ); 释放各个节点占用的空间

Page 42: 第七章  结构和模块化设计 7.1   结构

主控逻辑的实现main( ) {struct Link *pHead = NULL;int c; char no[32];while( 1 ) {c = GetCommand( ); /* 命令输入 */switch( c ) {case 1: pHead = InputStudent( );break;/* 信息输入 */case 2: OutputStudent( pHead );break;/* 信息输出 */case 3: scanf( “%s”, no );pHead = RemoveStudent( pHead, no );break;/* 信息删除 */default: DeleteLink( pHead );return 0; /* 释放链表空间 */

} } }

Page 43: 第七章  结构和模块化设计 7.1   结构

命令输入的实现int GetCommand( void ) {int c;do {

printf( “Select an function as follow:\n” );printf( “1 for Input\n” );printf( “2 for Output\n” );printf( “3 for Remove\n” );printf( “0 for Exit\n” );printf( “Input(0-3): “ );scanf( “%d”, &c );

} while( c < 0 || c > 3 ); /* 消除非法输入 */return c;

}

Page 44: 第七章  结构和模块化设计 7.1   结构

信息输出(遍历链表)void OutputStudent( struct Link *p ) {

while( p != NULL ) {printf( “\n%s, %ld, %s, %s”,p->data.student, /* 学号 */p->data.no, /* 身份证号 */p->data.college, /* 学院 */p->data.class ); /* 班级 */p = p->next; /* 指向下一节点 */

} }

Page 45: 第七章  结构和模块化设计 7.1   结构

链表的处理方法 元素的查找方法

沿着节点指针,逐个查找 比数组效率低

元素的删除方法 改变元素的指针连接关系 比数组效率高

元素的插入 选择位置,改变指针连接关系 比数组效率高

Page 46: 第七章  结构和模块化设计 7.1   结构

信息删除(链表元素的删除)

struct Link *RemoveStudent( struct Link *p, /* 表头指针 */char *n ) /* 学号 */

{struct Link *q;if( NULL == p )return NULL;if( 0 == strcmp(p->data.student, n) ) {

q = p; /* 头元素 = 指定学生 */p = p->next; /* 指向其余元素 */delete q; /* 释放头元素空间 */} elsep->next = RemoveStudent( p->next, n );/* 在其余表中删除(递归法) */return p; /* 修改后的链表 */

}

Page 47: 第七章  结构和模块化设计 7.1   结构

动态数据结构的释放void DeleteLink( struct Link *p ) {

struct Link *q;while( p != NULL ) {

q = p;p = p->next; /* 指向下一节点 */delete q; /* 释放当前节点 */

}}

Page 48: 第七章  结构和模块化设计 7.1   结构

动态数据结构的使用 应用中的问题

面向复杂算法和复杂数据结构 动态数据结构的全局性破坏信息隐蔽 出现专门用于组织数据的数据指针

如何控制复杂性 使用抽象手段 模块化、函数抽象、局部化 仍需要研究新的抽象手段

Page 49: 第七章  结构和模块化设计 7.1   结构

本章作业 阅读第十章

完成自我测验练习 10.3 10.4 程序设计练习

12.7 12.91. 为时间(小时、分、秒)的保存设计一个结构;编写程序,读入形如 21:32:04 的输入,保存在结构中,检查输入数据的正确性。2. 修改程序例 7-3 :在学籍信息中添加入学日期,修改程序中相应的部分。

上机题 修改例 7-4 中函数 InputStudent ,保证链表中各个学生的信息是按照学号升序的顺序组织的 连同例 7-5 完成程序测试。