本章主要讲述了<函数与模块化程序设计>
一、函数的定义
1.程序都是从main()的入口开始,到main()的出口结束。
函数可分为标准库函数和自定义函数。
2.使用ANSI C的库函数时,必须在程序的开头把该函数的头文件包含起来。
3.函数使用前必须先定义。
格式:
返回值类型 函数名(类型 形式参数1,类型 形式参数2,...)
{
声明语句序列
可执行语句序列
}
4.通常变量名用小写字母开头的单词组合而成,使用"名词"或者"形容词+名词"的形式。
函数名用大写字母开头的单词组合而成,使用"动词"或者"动词+名词"的形式。
5.在函数体内部定义的变量只能在函数体内访问,称为内部变量,也是形式参数。
6.若函数没有函数返回值,则需用void定义返回值的类型。
注意:在函数定义的前面写上一段注释来描述函数的功能及其形参,是一个非常好的编程习惯。
二、向函数传递值和从函数返回值
1.函数不能嵌套定义。
2.函数的返回值只能有一个,函数返回值的类型的可以是除数组以外的任何类型。函数中的return语句可以有多个,但不表示函数可以有多个返回值。
3.当函数返回值类型为void类型时,表示函数没有返回值,函数可以没有return 语句,程序一直运行到函数的最后一条语句后再返回。如果程序不是运行到函数的最后一条语句才返回,那么必须使用return 语句返回,无须返回任何值的return语句可写成
return;
4.若定义main()时未显示指明其返回值类型,也未使用void,则其返回值将被默认为int类型。
5.自定义函数例子:
注意:当函数在主函数后面时,要在主函数里面添加声明。
6.函数的数量必须与形参相等,它们的类型必须匹配,匹配的原则与变量赋值的原则一样。
三、函数的递归调用和递归函数
1.如果一个对象部分地由它自己组成或按它自己定义,则我们称它是递归。
2.经典例子:
3.任何递归函数都必须至少有一个基线情况,并且一般情况必须最终能转化为基线情况,否则程序将无线递归下去,导致程序出错。
4.每个迭代程序原则上都可以转换成等价的递归程序,反之不行。
四、变量的作用域和生存期
1.变量的作用域规则:每个变量仅在定义它的语句块内有效,并且拥有自己的存储空间。
2.全局变量的作用域为整个程序,即全局变量在程序的所有位置均有效。
3.全局变量未赋值时,自定义初始化为0。
4.形参值的改变不会影响实参,因为实参与形参分别占据着不同的存储单元。
5.在并列的语句块之间只能通过一些特殊通道传递数据,如函数参数、返回值,以及全局变量。
6.变量的存储类型一般声明如下:
存储类型 数据类型 变量名表;
7.自动变量(动态局部变量)
标准定义格式:
auto 类型名 变量名;
如果没有指定变量的存储类型,那么变量的存储类型就缺省为auto。
特点: (1)自动变量在定义时不会自动初始化;
(2)自动变量在退出函数后,其分配的内存立即被释放。
8.静态变量
标准定义格式:
static 类型名 变量名;
特点:在下一次进入函数时,静态局部变量的值仍保持上一次退出函数前所拥有的值。
9.外部变量
声明格式:
extern 类型名 变量名;
作用域是它的定义点到本文件的末尾。
和静态变量一样,外部变量也是在静态存储区内分配内存的,其生存期是整个程序的运行期。没有显示初始化的外部变量由编译程序自动初始化为0。
静态局部变量只能在定义它的函数内被访问,静态全局变量可以在定义它的文件内的任何地方被访问,但不能像非静态的全局变量那样被程序的其他文件所访问。
10.寄存器变量
定义格式:
register 类型名 变量名;
它可以避免CPU对存储器的频繁数据访问,使程序更小、执行速度更快。
五、模块化程序设计
1.模块分解的基本原则:高聚合、低耦合,保证每个模块的相对独立性。
2.把需要共享的函数放在一个单独的.c文件中,把共享函数的函数原型、宏定义和全局变量声明等放在一个单独的.h头文件中,其他需要共享这个函数的程序用#include包含这个头文件后,就可以调用这个函数了。因头文文件<stdio.h>几乎在每个文件中都要使用,因此几乎每个文序设 件都要包含这个头文件。
3.对于系统提供的标准库函数的头文件,通常用尖括号包含头文件。
4.头文件里对全局变量的声明要加上extern关键字,用以声明该变量为外部变量。变量声明与变量定义不同的是:对于变量声明,编译器并不对其分配内存,因为这个变量实际是在其他模块定义的,即希望这个变量的内存是在其他模块分配的,用extern声明表示要使用在其他模块定义的变量。如果模块内不允许使用模块外定义的函数和全局变量,只要在定义它们时加上static关键字,就能保证只能在该模块内使用它们了。
5.assert()的功能是在程序的调试阶段验证程序中某个表达式的真与假,使用assert()的源文件中只需包含头文件<assert.h>即可。
6.在下面几种情况中才考虑使用断言:
(1)检查程序中的各种假设的正确性,例如一个计算结果是否在合理的范围内。
(2)证实或测试某种不可能发生的状况确实不会发生,例如一些理论上永远不会执行到的分支(如switch的default:后)确实不会被执行。
assert(断言)仅能用于调试程序,不能作为程序的功能。