第 8讲 模块化程序设计
请大家 及时消化 我课上讲的内容,并举一反三,模仿未讲过的例题多写程序。
如果说学习 C语言有捷径的话:那就是 多读程序,多写程序
2
#include<math.h>
#include<stdio.h>
void main()
{int i,a,b,c;
for(i=100;i<=999;i++)
{ a=i/100; /*求百分位 */
b=(i-100*a)/10; /*求十分位 */
c=i%10; /*求个位 */
if(pow(a,3)+pow(b,3)+pow(c,3)==i)
printf("%d is a shui xian hua shu!",i);
}
}
上周作业答案 1
3
#include<math.h>
#include<stdio.h>
void main()
{int i,j,k,num;
for(i=1;i<=9;i++) /*列举百分位 */
for(j=0;j<=9;i++) /*列举十分位 */
for(k=1;k<=9;k++) /*列举个位 */
{ num=i*100+j*10+k;
if(pow(i,3)+pow(j,3)+pow(k,3)==num)
printf("%d is a shui xian hua shu!",num);
}
}
上周作业答案 2
4
怎样解决一个复杂的问题?
简单问题:
分析问题?写算法?编写程序?调试
大型软件开发的过程大致分为,系统定义、
需求分析、系统设计、编写程序、系统测试、系统维护 等阶段。这些工作不可能由一个人在短时间内完成。
模块化程序设计的主要思想
,自顶向下、逐步求精,
5
例:高校信息管理系统功能分解高校信息管理系统人事管理子系统设备管理子系统教学管理子系统财务管理子系统学生管理子系统 ……
系统管理 学籍管理 班级管理 成绩管理 数据查询 综合测评 ……
用户管理退出系统录入信息修改信息录入信息修改信息录入信息修改信息学籍查询班级查询成绩查询
……
函数一个源程序文件
*.c
一个项目
*.prj
6
C程序结构
C程序
(*.prj)
源程序文件 1(*.c) 源程序文件 i(*.c) 源程序文件 n(*.c)
预编译命令 函数 1 函数 i 函数 n
函数声明部分 函数执行部分
7
为什么模块化?
1、大任务要分成小的模块,以利于任务的分配。
2、模块化可以实现程序代码的 复用,提高编程效率 。
8
模块化的几个原则
模块分解的原则
保证模块的相对独立性
高聚合:一个模块只能完成单一的功能,代码一般几十行。
低耦合:指模块之间参数传递尽量少,尽量不通过全局变量来实现数据传递
信息隐藏
把所有用户不需要关心的细节隐藏至模块内部。
9
我们怎么做?
关键是如何,分段,。
比较独立的、完整的功能分为一个函数,
一般函数十几行。
函数定义时注意与被调函数之间的沟通与联系,即参数传递与返回两个方向的数据流动。
在讲例题的时候请注意这两点
10
例 5-1:计算三个数最大数与最小数的差
#include <stdio.h>
int dif(int x,int y,int z);
int max(int x,int y,int z);
int min(int x,int y,int z);
void main()
{ int a,b,c,d;
printf("Input Data,");
scanf("%d%d%d",&a,&b,&c);
d=dif(a,b,c);
printf("Max-Min=%d\n",d);
}
int dif(int x,int y,int z) /* 定义 dif函数求三数的差值 */
{ int m1,m2;
m1= max(x,y,z);
m2= min(x,y,z);
return m1-m2;
}
int max(int x,int y,int z) /* 定义 max函数求三数的最大值 */
{ int r1,r2;
r1=(x>y)?x:y;
r2=(r1>z)?r1:z;
return(r2);
}
int min(int x,int y,int z) /* 定义 min函数求三数的最小值 */
{ int r;
r=(x<y)?x:y;
return(r<z?r:z);
}
程序的执行顺序:
main( )
调用函数 dif
输出结束
dif函数 max函数调用函数 max
调用函数 min min函数
1 2 3 4
56
8 9
1011
1213
14
嵌套调用
多个函数时使用这种方法将多个函数连在一起,构成整个程序。
读程序时要按这个顺序运行程序
11
例 5-5
例:求 n!= 1 n=1
n*(n-1)! n>1
long fac(int n)
{ if(n==1) return 1;
else return n*fac(n-1);
}
写成函数?
12
例 5-5程序
#include<stdio.h>
long fac(int n)
{ if(n==1)
return 1;
else
return n*fac(n-1);
}
main()
{int n;
Printf(“please input a number:\n”);
scanf(“%d”,&n);
printf(″%2d!=%ld″,n,fac(n));
}
f(10)
{…
retrun 10*f(9 );
… }
f(9)
{…
retrun 9*f(8 );
… }
f(1)
{…
return 1;
… }
f(2)
{…
return 2*f(1);
… }

执行过程?
调用函数 递推依次返回 回归
递归:函数嵌套调用自身
13
多函数程序的调试方法
要想检验一个函数是否正确,要在调用函数处设置断点。当调试执行时,在该行停止,此时,点击 build->step into
进入函数内部执行,此时可点击 build-
>step over单步跟踪执行,或在调试前在函数内部重点检查语句行设置断点。
若想结束函数内部的执行,点击 build-
>step out,回到主调函数。
14
递归 迭代
long int fac(int n)
{ if(n<=1)
return 1;
else
return n*fac(n-1);
}
long int fac(int n)
{Long fc=1; int i;
for(i=1;i<=n;i++);
fc=fc*i;
return fc;
}n!= 1 n=1
n*(n-1)! n>1 n!=(… ((1*2)*3)… *n)
15
递归与迭代的比较递 归 迭 代控制结构 选择结构 循环结构终止条件 边界条件 循环条件求解方向 n?n-1?…?1 1?2?…?n
优点 解题思路简单缺点 递归调用执行过程复杂,
效率低效率高例,n!= 1 n=1
n*(n-1)! n>1
n!=(… ((1*2)*3)… *n)
16
什么问题可以用递归来解决?
问题具有如下特点:
问题较复杂,不易直接求解
该问题可以分解成若干子问题,子问题除了规模较原问题小以外,其它均相同。
最终,总有一个问题不能再分解。
复杂问题 边界问题
n n=1或 2规模减小依次递推求得复杂问题的解
17
例 5-4 求解 Hanoi(汉诺)塔问题
古代有一个梵塔,塔内有三个柱子 A,B、
C,僧侣们想把 A拄子上的一摞盘子移动到 C柱子上。最初 A拄子上有大小不等的
64个盘子,且小的在上,大的在下。在移动过程中,大盘子只能在下,小盘子只能在上,并且每次只能移动一个盘子,
可以借助于 B柱子。
6463
62
1
A B C
18
问题分析
解决 Hanoi(汉诺)塔问题的方法可以表述如下:
⑴老和尚移动 64个盘子的步骤第 1步,请第 2个和尚将前 63个盘子从 A柱子移到 B柱子;
第 2步,自己将最下面的第 64个盘子从 A柱子移到 C柱子;
第 3步,再请第 2个和尚将 63个盘子从 B柱子移到 C柱子。
⑵第 2个和尚移动 63个盘子的步骤第 1步,请第 3个和尚将前 62个盘子从 A柱子移到 C柱子;
第 2步,自己将最下面的第 63个盘子从 A柱子移到 B柱子;
第 3步,再请第 3个和尚将 62个盘子从 C柱子移到 B柱子。
依此类推,直到第 63个和尚完成了 2个盘子的移动,最后由第 64个和尚完成 1个盘子的移动。这个过程称之为,回推,过程。
每个人的工作是:移动 n个盘子从 A到 B(借助 C)的步骤
第 1步,请别人将 n-1个盘子从 A柱子移到 C柱子;
第 2步,自己将最下面的第 n个盘子从 A柱子移到 B
柱子;
第 3步,再请别人将 n-1个盘子从 C柱子移到 B柱子。
一个特例:当 n==1的时候
19
move函数
/* 定义函数:显示移动过程
int no:表示第 no个盘子
char from:表示源柱子
char to:表示目的柱子
*/
void move(int no,char from,char to)
{
printf("Move %3d,%c ――>
%c\n",no,from,to);
}
20
hanoi函数
/* 定义函数:
借助 by柱子将 n个盘子从 from柱子移动到 to柱子
int n:表示 n个盘子 char from:表示源柱子
char to:表示目的柱子,char by:表示要借助的柱子
*/
void hanoi(int n,char from,char by,char to)
{ if (n==1)
move(n,from,to);
else
{ hanoi(n-1,from,to,by);
move(n,from,to);
hanoi(n-1,by,from,to);
}
}
21
#include <stdio.h> /* 包含头文件 */
void move(int,char,char); /* 自定义函数的声明 */
void hanoi(int n,char,char,char); /* 自定义函数的声明 */
void main() /* 主函数,无参数,无返回值 */
{ int n; /* 定义整型变量 n,存放盘子总数 */
printf("Input the number of diskes,"); /* 提示输入 n的值 */
scanf("%d",&n); /* 输入 n的值 */
printf("The step to moving %3d diskes:\n",n);
hanoi(n,'A','B','C'); /* 借助 B柱子将 n个盘子从 A移到 C */
}
/* 定义函数:显示第 no个盘子的移动过程,从 from到 to */
void move(int no,char from,char to)
{
printf("Move %3d,%c ――> %c\n",no,from,to);
}
/* 定义函数:借助 by柱子将 n个盘子从 from柱子移动到 to柱子 */
void hanoi(int n,char from,char by,char to)
{ if (n==1)
move(n,from,to);
else
{ hanoi(n-1,from,to,by);
move(n,from,to);
hanoi(n-1,by,from,to);
}
}
完整程序
执行过程?
请大家用讲过的调试方法一下 n为 3的执行过程
22
运行输出:
Input the number of disks:
3
The step to moving 3 disks:
Move 1,A --> B
Move 2,A --> C
Move 3,B --> C
Move 4,A --> B
Move 5,C --> A
Move 6,C --> B
Move 7,A --> B
23
语法:函数的 递归调用
在调用一个函数的过程中又直接或间接地调用函数本身
f( )
{…
f( );
… }
f1( )
{…
f2( );
… }
f2( )
{…
f1( );
… }
特殊形式的函数嵌套重点直 接递归 间 接递归
24
练习:例 5-6
fib(n)=
n ( n= 0,1) /* 递归结束条件 */
fib(n-2)+ fib(n-1) ( n>1) /* 递归方式 */
25
例 5-6程序
#include<stdio.h>
long fib(int n); /* 自定义函数声明 */
void main()
{ long s; /* 第 i 项斐波那契数列的值 */
int i=0; /* 斐波那契数列某项的序号 */
do
{ s=fib(i); /* 求斐波那契数列第 i 项 */
printf("Fib(%d)=%ld\n",i,s); /* 显示斐波那契数列第 i 项的值 */
printf("Input Fibonacci Number,0 means exiting:");
scanf("%d",&i); /* 输入要求的某一项的序号 */
}while(i>0); /* 循环输入序号,直到 i值小于等于 0 */
}
/* 定义求第 n项斐波那契数列值的函数 */
long fib(int n)
{ if(n==0||n==1) /* 判断是否为结束条件 */
return n;
else
return fib(n-2)+fib(n-1); /* 求斐波那契数列的递归方式 */
}
26
本讲小结
多函数程序设计
什么情况下需要定义多函数
函数的定义、调用、声明格式
函数的嵌套调用和递归调用
理解调用过程(程序的执行过程)
什么情况下用递归,递归的解题思路是什么?
下节课讲函数的存储类型和第六章,请提前预习
27
作业:
习题 5.6,5.12(提示:可仿照 5.6题,
用递归完成 )
自学例题:求解 Hanoi(汉诺)塔问题。
关键是要搞清楚递归函数的写法 (即如何将 n的问题转化为 n-1的问题 ),以及递归调用的过程 (输入 3试跟踪一下调用过程 )