第四章
模块化程序设计
?C语言的模块化功能,
?函数式的程序结构:程序整体由一个或多个函数构成,
每个函数具有各自独立的功能和明显的界面。
?允许使用不同存储类别的变量,控制模块内部和外部
的信息交换。
?具有编译预处理功能,为程序的调试、移植提供了方
便。
?支持模块化程序设计。
函数
一,C程序结构
?用 C语言编写程序的任务:编写函数。至少要有
一个 main()函数。
?C语言执行,C语言的执行就是执行 main()函数,
从开始 main()函数的第一个花括号开始,到最后
一个花括号结束。其它函数,均是在 main()被执
行时才被调 用。
?C语言程序设计方法:自顶向下、逐步细化。
?先集中考虑 main()中的算法,某些功能先用一个函数表
达式表示。
?main()设计完毕,再考虑所要用到的函数,有的直接拿
来使用,没有的就开始动手设计。
结构化程序设计举例
高校事务管理系统
…… …… …… …… …… ……
办
公
室
管
理
教
务
管
理
科
研
管
理
人
事
管
理
财
务
管
理
后
勤
管
理
图
书
管
理
……
学
藉
管
理
成
绩
管
理
排
课
管
理
……
多级管理的实现:一般用菜单方式(例 L4-1)
高校事务管理系统
1、办公室管理 2、教务管理
3、科研管理 4、人事管理
5、财务管理 6、后勤管理
7、图书管理 8,退出系统
请您选择功能( 1-8),
二、函数的定义与声明
? 例 4-2
main()
{…
double fun(int a,int b);
…
}
Double fun(int a,int b)
{
/*函数体 */
}
函数的声明
,;”不能少
函数的定义
不能有,;”
(一)函数的定义
? 函数名:函数名要符合 C的标识的规定。
? 形式参数,
? 要在函数名后的括号内,有多个时,以逗号分隔。
? 形式参数只有在程序运行时才临时分配存储空间,用完则释放该空
间,因此函数中的参数值的变化不返回(例 L4-2-2),称为值传递。
? 函数体:是一个分程序结构,和主程序一样,一般也由变
量定义部分和语句组成。但允许只有语句甚至整个函数为
空。
? 函数的返回值,
? 有返回值,则要声明函数的类型,并用 return返回其值。
? 没有返回值,则函数类型声明为 void型,且若函数中的 return在最
后时,可省去。
? 函数的外部特性,
? 函数不允许嵌套:函数是平等的,不能在函数中再定义函数。
(二)函数的声明
?在主调函数中,要对在本函数中将要调用的函数
事先作一声明。其作用是向编译系统提供必要的
信息:函数名,函数类型,参数的个数、类型及
排列次序。并注意这些应与函数的定义部分一一
对应(一般只要将函数的定义复制过来,并加一
个,;”即可。)
?虽然,在主调函数前定义的函数,可不声明,但
一般还是养成声明的习惯为好,这样可以清楚地
知道,在主调 函数中到底要用到哪些函数。
三、函数的嵌套调用
? C语言中允许函数中再调用函数。
{…,
u=f1(i,t)
…,
}
main( )函数
{…,
v=f2(p,q,t)
…,
}
f1( )函数
{
…,
}
f2( )函数
1 2 3
4
5
6
7
四、函数的递归调用
? 函数的递归调用就是一个函数直接或间接地调用它自身。
? 例 L4-9 求 n!
要求 n!可以,
? 只先求出 (n-1)!,再乘以 n。
? 要求 (n-1)!,只先求出 (n-2)!,再乘以 n-1。
? ……
? 终于变成了求 1!(这个值是已知的,也叫递归终止条件)
? 再回推过去。
? 这就是一个递归调用问题。
例 L4-10 -2 汉诺塔问题:将 64个盘子从从 A座移至 C座,可
以借助 B座。
? 将 n个盘子从 A座移至 C座的解决的方法,
1,将上面的 n-1个盘子从 A移至 B;
2,将剩下的一个从 A移至 C;
3,将 n-1个从 B移至 C;
? 其中的将 n-1个盘子从一座移至另一座,又重复前面的过
程,只是盘子数减 1。且第 1步和第 3步性质相同,只是座
名不同而已。
A B C
void hanoi(int n,char a,char b,char c)
{
if(n>0)
{hanoi(n-1,a,c,b); /*将 n-1个盘子借助 c,从 a移至 b */
printf("\n Move disc %d from pile %c to pile %c",n,a,c);
getch(); /*将第 n个盘子从 a移至 c*/
hanoi(n-1,b,a,c); /*将 n-1个盘子借助 a,从 b移至 c*/
}
}
main()
{int n;
clrscr();
printf("n=");
scanf("%d",&n);
hanoi(n,'a','b','c');
}
变量的存储属性
?变量除类型外,还有存储属性。
?变量的存储器属性
?变量的生存周期
?变量的可用域(可见性)
存储属性 register auto static extern
存储位置 寄存器 主存
生存期 动态生存期 永久生存期
作用域 局部 局部或全局 全局
一、动态变量
? 动态变量是在程序执行的某一时刻被动态地建立,
并在另时刻被动态地撤消的一种变量。它们存在
于程序的局部,也只在这个局部中以用。有两种,
?自动变量 (auto)
?寄存器变量 (register)
?自动变量:是 C中用的最多的变量,因为建立和
撤消这些变量都是由系统在程序执行中自动完成
的。自动变量定义,
?格式,[auto] 数据类型 变量名 [=初值表达式 ],… ;
?特点,
?自动变量是局部变量。
?在对变量赋初值前,其值是不确定的。
例 L4-11-0, L4-11-1,L4-13(多次调用同一个函数)
?寄存器变量:与自动变量的性质完全相同,只是
存放的位置是在 CPU的寄存器中。通常将使用频
率高的变量定义为寄存器变量。
?寄存器变量的定义,
register 数据类型 变量名 [=初值表达式 ],… ;
?说明,
?在程序中,遇到寄存器变量时,系统会努力去实现它,但因
寄存器数量有限,不能实现时,会自动当作自动变量来处理。
?一般情况下用与不用寄存器变量很难看出差别,只有当运算
量特别大时,才会有明显差别。
例 L4-14
二、静态变量
?静态变量的存储空间在程序的整个运行期间是固
定的,并在编译时就为其分配了存储空间,直到
程序结束都存在。
?静态变量的初始化:在编译时进行,在定义时可
使用常量或常量表达式进行初始化;未初始化时,
自动取 0为其初值。
?多次调用含静态变量的函数时,静态局部变量具
有继承性。
?静态局部变量的值只能在本函数(分程序叶)中
使用。
例 L4-15 用与不用 static的区别。
三、外部变量
?外部变量:定义在所有函数之外的变量叫外部变
量。
?外部变量是全局变量,其作用域是从定义点开始,
到本文件末尾。 例 L4-16 交换两个变量的值。
?外部变量使用的几种情况,
?加 static,则静态外部变量,只能在本文件中使用。
例 L4-17
?对于位定义点之前的函数,可以用 extern说明符将变量
的作用域扩充到需要它的地方。 例 L4-18
?在其它需要用到这些外部变量的文件中,对变量用
extern作声明,可将外部变量的作用域扩充到其它文件
中。 例 File2.c file1.c
外部变量的负作用
?外部变量会使得程序的关系变得复杂,变量的取
值与变化难以控制,造成模块间联系太多,对外
部依赖太多,降低了模块的独立性,给设计、调
试、维护带来困难。因此,应限制外部变量的使
用。
?例 L4-19( L4-19-1 L4-19-2)
预处理
?预处理是在编译前对源程序进行一些加工。
?C的预处理均以,#”开头,末尾不加分号,以区
别于 C的语句。
?它可以出现在程序的任位置,其作用域为出现处
至源程序末尾。一般放在程序首部。
?预处理的作用:能改善程序设计的环境,有助于
编写易于移植的程序,也是模块化程序设计的一
个工具。
?已学习过的有,#include和 #define
一、宏替换
?宏替换是用预处理命令 #define指定的预 处理。有
两种,
?字符串宏替换
?带参数的宏替换
?字符串宏替换,
?格式,#define 宏名 宏体
?功能:预处理时,将程序中所有的宏名用宏体替换。
如符号常量的定义。
?好处,
?提高程序的可读性
?提高程序的易改性
例 L4-20 L4-21
?带参数宏,
?格式,#define 宏名 (形参表 ) 宏体
?例 L4-21-2
?注意,
?宏体中参数最好用括号括起来,否则易出错。
例,L4-21-3
?宏可以嵌套:例 L4-23
?宏替换只是简单的字符替换,不进行计算(与函数不同),
不太好理解(副作用)。
例,L4-25 L4-25-2
书上 P133的注意事项。
二、文件包含
?文件包含:是通过 #include 把另一个文件的整个
内容嵌入至当前文件中,实际上是宏替换的延伸。
?两种格式,
?#include,文件”,先到源文件所在目录下找,再到系
统指定的 include文件目录下找。
?#include <文件 >,只到系统指定的 include文件目录下
找。
?C中标准头文件及功能,P135表 4.3
模块化程序设计
?C语言的模块化功能,
?函数式的程序结构:程序整体由一个或多个函数构成,
每个函数具有各自独立的功能和明显的界面。
?允许使用不同存储类别的变量,控制模块内部和外部
的信息交换。
?具有编译预处理功能,为程序的调试、移植提供了方
便。
?支持模块化程序设计。
函数
一,C程序结构
?用 C语言编写程序的任务:编写函数。至少要有
一个 main()函数。
?C语言执行,C语言的执行就是执行 main()函数,
从开始 main()函数的第一个花括号开始,到最后
一个花括号结束。其它函数,均是在 main()被执
行时才被调 用。
?C语言程序设计方法:自顶向下、逐步细化。
?先集中考虑 main()中的算法,某些功能先用一个函数表
达式表示。
?main()设计完毕,再考虑所要用到的函数,有的直接拿
来使用,没有的就开始动手设计。
结构化程序设计举例
高校事务管理系统
…… …… …… …… …… ……
办
公
室
管
理
教
务
管
理
科
研
管
理
人
事
管
理
财
务
管
理
后
勤
管
理
图
书
管
理
……
学
藉
管
理
成
绩
管
理
排
课
管
理
……
多级管理的实现:一般用菜单方式(例 L4-1)
高校事务管理系统
1、办公室管理 2、教务管理
3、科研管理 4、人事管理
5、财务管理 6、后勤管理
7、图书管理 8,退出系统
请您选择功能( 1-8),
二、函数的定义与声明
? 例 4-2
main()
{…
double fun(int a,int b);
…
}
Double fun(int a,int b)
{
/*函数体 */
}
函数的声明
,;”不能少
函数的定义
不能有,;”
(一)函数的定义
? 函数名:函数名要符合 C的标识的规定。
? 形式参数,
? 要在函数名后的括号内,有多个时,以逗号分隔。
? 形式参数只有在程序运行时才临时分配存储空间,用完则释放该空
间,因此函数中的参数值的变化不返回(例 L4-2-2),称为值传递。
? 函数体:是一个分程序结构,和主程序一样,一般也由变
量定义部分和语句组成。但允许只有语句甚至整个函数为
空。
? 函数的返回值,
? 有返回值,则要声明函数的类型,并用 return返回其值。
? 没有返回值,则函数类型声明为 void型,且若函数中的 return在最
后时,可省去。
? 函数的外部特性,
? 函数不允许嵌套:函数是平等的,不能在函数中再定义函数。
(二)函数的声明
?在主调函数中,要对在本函数中将要调用的函数
事先作一声明。其作用是向编译系统提供必要的
信息:函数名,函数类型,参数的个数、类型及
排列次序。并注意这些应与函数的定义部分一一
对应(一般只要将函数的定义复制过来,并加一
个,;”即可。)
?虽然,在主调函数前定义的函数,可不声明,但
一般还是养成声明的习惯为好,这样可以清楚地
知道,在主调 函数中到底要用到哪些函数。
三、函数的嵌套调用
? C语言中允许函数中再调用函数。
{…,
u=f1(i,t)
…,
}
main( )函数
{…,
v=f2(p,q,t)
…,
}
f1( )函数
{
…,
}
f2( )函数
1 2 3
4
5
6
7
四、函数的递归调用
? 函数的递归调用就是一个函数直接或间接地调用它自身。
? 例 L4-9 求 n!
要求 n!可以,
? 只先求出 (n-1)!,再乘以 n。
? 要求 (n-1)!,只先求出 (n-2)!,再乘以 n-1。
? ……
? 终于变成了求 1!(这个值是已知的,也叫递归终止条件)
? 再回推过去。
? 这就是一个递归调用问题。
例 L4-10 -2 汉诺塔问题:将 64个盘子从从 A座移至 C座,可
以借助 B座。
? 将 n个盘子从 A座移至 C座的解决的方法,
1,将上面的 n-1个盘子从 A移至 B;
2,将剩下的一个从 A移至 C;
3,将 n-1个从 B移至 C;
? 其中的将 n-1个盘子从一座移至另一座,又重复前面的过
程,只是盘子数减 1。且第 1步和第 3步性质相同,只是座
名不同而已。
A B C
void hanoi(int n,char a,char b,char c)
{
if(n>0)
{hanoi(n-1,a,c,b); /*将 n-1个盘子借助 c,从 a移至 b */
printf("\n Move disc %d from pile %c to pile %c",n,a,c);
getch(); /*将第 n个盘子从 a移至 c*/
hanoi(n-1,b,a,c); /*将 n-1个盘子借助 a,从 b移至 c*/
}
}
main()
{int n;
clrscr();
printf("n=");
scanf("%d",&n);
hanoi(n,'a','b','c');
}
变量的存储属性
?变量除类型外,还有存储属性。
?变量的存储器属性
?变量的生存周期
?变量的可用域(可见性)
存储属性 register auto static extern
存储位置 寄存器 主存
生存期 动态生存期 永久生存期
作用域 局部 局部或全局 全局
一、动态变量
? 动态变量是在程序执行的某一时刻被动态地建立,
并在另时刻被动态地撤消的一种变量。它们存在
于程序的局部,也只在这个局部中以用。有两种,
?自动变量 (auto)
?寄存器变量 (register)
?自动变量:是 C中用的最多的变量,因为建立和
撤消这些变量都是由系统在程序执行中自动完成
的。自动变量定义,
?格式,[auto] 数据类型 变量名 [=初值表达式 ],… ;
?特点,
?自动变量是局部变量。
?在对变量赋初值前,其值是不确定的。
例 L4-11-0, L4-11-1,L4-13(多次调用同一个函数)
?寄存器变量:与自动变量的性质完全相同,只是
存放的位置是在 CPU的寄存器中。通常将使用频
率高的变量定义为寄存器变量。
?寄存器变量的定义,
register 数据类型 变量名 [=初值表达式 ],… ;
?说明,
?在程序中,遇到寄存器变量时,系统会努力去实现它,但因
寄存器数量有限,不能实现时,会自动当作自动变量来处理。
?一般情况下用与不用寄存器变量很难看出差别,只有当运算
量特别大时,才会有明显差别。
例 L4-14
二、静态变量
?静态变量的存储空间在程序的整个运行期间是固
定的,并在编译时就为其分配了存储空间,直到
程序结束都存在。
?静态变量的初始化:在编译时进行,在定义时可
使用常量或常量表达式进行初始化;未初始化时,
自动取 0为其初值。
?多次调用含静态变量的函数时,静态局部变量具
有继承性。
?静态局部变量的值只能在本函数(分程序叶)中
使用。
例 L4-15 用与不用 static的区别。
三、外部变量
?外部变量:定义在所有函数之外的变量叫外部变
量。
?外部变量是全局变量,其作用域是从定义点开始,
到本文件末尾。 例 L4-16 交换两个变量的值。
?外部变量使用的几种情况,
?加 static,则静态外部变量,只能在本文件中使用。
例 L4-17
?对于位定义点之前的函数,可以用 extern说明符将变量
的作用域扩充到需要它的地方。 例 L4-18
?在其它需要用到这些外部变量的文件中,对变量用
extern作声明,可将外部变量的作用域扩充到其它文件
中。 例 File2.c file1.c
外部变量的负作用
?外部变量会使得程序的关系变得复杂,变量的取
值与变化难以控制,造成模块间联系太多,对外
部依赖太多,降低了模块的独立性,给设计、调
试、维护带来困难。因此,应限制外部变量的使
用。
?例 L4-19( L4-19-1 L4-19-2)
预处理
?预处理是在编译前对源程序进行一些加工。
?C的预处理均以,#”开头,末尾不加分号,以区
别于 C的语句。
?它可以出现在程序的任位置,其作用域为出现处
至源程序末尾。一般放在程序首部。
?预处理的作用:能改善程序设计的环境,有助于
编写易于移植的程序,也是模块化程序设计的一
个工具。
?已学习过的有,#include和 #define
一、宏替换
?宏替换是用预处理命令 #define指定的预 处理。有
两种,
?字符串宏替换
?带参数的宏替换
?字符串宏替换,
?格式,#define 宏名 宏体
?功能:预处理时,将程序中所有的宏名用宏体替换。
如符号常量的定义。
?好处,
?提高程序的可读性
?提高程序的易改性
例 L4-20 L4-21
?带参数宏,
?格式,#define 宏名 (形参表 ) 宏体
?例 L4-21-2
?注意,
?宏体中参数最好用括号括起来,否则易出错。
例,L4-21-3
?宏可以嵌套:例 L4-23
?宏替换只是简单的字符替换,不进行计算(与函数不同),
不太好理解(副作用)。
例,L4-25 L4-25-2
书上 P133的注意事项。
二、文件包含
?文件包含:是通过 #include 把另一个文件的整个
内容嵌入至当前文件中,实际上是宏替换的延伸。
?两种格式,
?#include,文件”,先到源文件所在目录下找,再到系
统指定的 include文件目录下找。
?#include <文件 >,只到系统指定的 include文件目录下
找。
?C中标准头文件及功能,P135表 4.3