分而治之就是把大问题分解为小问题来处理。模块化程序设计就是通过功能分解实现的,功能分解就是把大功能分解为小功能。函数是c语言模块化设计的最小单位,如果程序设计比作机器,那么函数就是机器的零件,这个零件可以是自己设计的,也可以是别人设计的。
1.0函数的定义
程序从main()入口开始到main()出口结束,中间不断的调用函数,他就好比一个主管,管理函数的使用。所以函数就有一个分类:
1.1函数的分类
标准库函数:比如前面使用的printf()函数,使用这些库函数,需要在程序开头把该函数所在的头文件包含起来,比如使用math.h中定义的fabs()函数,就要在程序开头将头文件<math.h>包含到程序中就可以啦。
自定义函数:自己编写的函数。开发团队里面可以互相共享自定义的函数,加快开发的进程。
1.2函数的定义
函数在使用前必须先定义,定义格式:
返回值类型 函数名(类型 形式参数1,类型 形式参数2,....)//这里是函数头部
{
函数体
}
函数名用来标明函数,说明函数的功能。函数名定义规则与变量名定义规则一样。在花括号里面的定义的变量只能在函数体里面使用,成为内部变量。形式参数又称为形参,也是内部变量。
形参表就是函数的入口,如果你不想定义函数返回值,那么就用void定义返回值类型。如果函数不需要入口参数,就用void代替表中的参数。
看例题:
用函数编写计算整数的阶乘:
long Fact(int n)
{
int i;
long result=1;
for(i=2;i<=n;i++)
{
result*=i;
}
return result;//return 后面的变量或者表达式的值代表函数要返回的值。
}
在函数定义的前面写上这个函数的功能的注释,是一个好的编码习惯。
7.3向函数传递值和从函数返回值
上面的例子不能使用,因为缺少main函数对他的调用,在调用函数的时候,必须要有一个实际参数的表达式给被调用的函数,调用其他函数的函数成为主调函数,被调用的函数就叫被调用函数。主调函数把实参的值复制给形参的过程,叫参数传递。
例子:编写main函数,调用函数Fact()来计算m!。其中,m的值由用户从键盘输入。
#include<stdio.h>
int main(void)
{
int m;
long ret;
printf("输入m:");
scanf("%d",&m);
ret=Fact(m);
printf("%d!=%ld\n",m,ret);
return 0;
}
养成一个良好的编程习惯,在函数后面都写上return,他的作用就是把返回的结果。
7.3.2函数原型
案例:将上面两个案例合成一个完整的程序
代码如下:
#include<stdio.h>
long Fact(int n);//函数的原型声明
int main(void)
{
int m;
long ret;
printf("输入m:");
scanf("%d",&m);
ret=Fact(m);
printf("%d!=%ld\n",m,ret);
return 0;
}
long Fact(int n)
{
int i;
long result=1;
for(i=2;i<=n;i++)
{
result*=i;
}
return result;//return 后面的变量或者表达式的值代表函数要返回的值。
}
在main函数上面那段是对函数原型的声明,按照这个形式,就可以调用函数。如果函数定义在调用函数的前面时,就可以不声明。
7.3.3函数封装与防御性程序设计
除啦函数能做什么,用户最关心他的接口含义,至于它的内部怎么定义的,算法之类的东西全被封装起来啦,这个用户没必要去关心这个就是函数的封装。
通常函数的接口设计好后,就不要去修改,可以修改函数里面的实现细节,只要函数的功能和接口不变,就不会影响他的调用者。
为啦让函数在不确定使用或非法数据输入时仍能保护自己避免出错,就有啦在函数入口处增加啦对函数参数合法性的检查,这个就是防御式编程。
就是检验传进来的参数是否是预想的。
实参的数量和类型必须与形参的匹配。