模块化与函数嵌套
计算机的最终走向是模拟人工智能和社会,人类在完成复杂任务都采用分工合作的方式,在计算机内部也可以通过函数来划分各程序的功能来完成一个复杂任务。
main函数就相当于程序里的皇帝,必须要有,并且只有一个。它指挥所有的大臣(子函数)协调工作,大臣又可以调用更底层的子函数,相当于指挥小兵再进行更具体的工作,这就叫函数嵌套:
程序1
函数的嵌套调用
// 21-1简单函数嵌套.c // #include <stdio.h> a() { printf("a函数开始 "); b(); printf("a函数结束 "); } b() { printf("b函数开始 "); } main() { printf("main函数运行开始 "); a(); printf("main函数运行结束 "); }
程序2
打印100-200间所有的素数
// 21-2函数求素数.c #include <stdio.h> //main() //{ // int i, j; //第一层循环变量 第二层循环变量 // int flag; //标志 1为素数 // for ( i = 100; i <= 200; i++) //求素数的范围 100 - 200 开始 // { // flag = 1; //当做一个标志使用 // for ( j = 2; j < i; j++) // { // if (i%j == 0) //素数的判定方式是能整除就不是素数 // { // flag = 0; // break; // } // } // if (flag == 1)printf("%d ",i); // } //} //使用函数 int fun(int i) { int j, flag = 1; for (j = 2; j < i; j++) { if (i%j == 0) { flag = 0; break; } } return flag; } main() { int i; for (i = 100; i <= 200; i++) if (fun(i) == 1) printf("%d ",i); }
再论数据传递
int fun(int n)
{
int m;
m=n+2;
return m;
}
main()
{
int i=3,j;
j=fun(2)*fun(i)+6;
}
第一次调用:
定义:fun(int n); /*形参*/
调用:fun(2); /*实参*/
这个实参到形参的传递过程可以分解为
int n=2;
第二次调用
定义:fun(int n); /*形参*/
调用:fun(i); /*实参*/
这个实参到形参的传递过程可以分解为
int n=i;
返回值没有形参和实参的说法,返回细节也比参数传递复杂,在这里不作详解。如果在表达式中出现函数调用,直接用它的返回值代替函数即可,如:
j=fun(2)*fun(i)+6;
两次返回值传递后得:
j=4*5+6;
程序3
求3个数中的最大数
// 21-3函数求三个数最大值.c #include <stdio.h> double max(double a,double b) { return a > b ? a : b; } main() { double a, b, c; printf("请输入三个数: "); scanf_s("%lf%lf%lf", &a, &b, &c); printf("%lf ", (max(a, (max(a, b))))); }
按数据类型的精度来排序,顺序如下(不考虑unsigned):
char、short、int、long、float、double、long double,如果形式参数比实际参数精度高,系统会自动转换成正确的类型,比如整数5会被转换成浮点的5.0。反过来如果形式参数精度比实际参数低,系统也不会报错,但是所得到的数值无法预料。
比如:
void fun(int a){…}
下面的代码:
short s;
fun(s);
没有问题,但是:
double d;
fun(d);
编译可以通过,程序在执行却会有无法预料的后果。
递归函数
函数还可以自己调用自己,这样的函数称作“递归函数”。
程序4
用递归函数求10!
// 21-4递归函数.c #include <stdio.h> int fun(int n) { int t = 1; if (n > 0) t = n * fun(n - 1); //递归调用 return t; } main() { printf("%d ", fun(10)); }
※注意:递归函数必须要设条件返回,否则它会无限递归下去,直至程序崩溃。
函数的设计原则
函数在设计时应该遵循两种原则:一种是自顶向下,也就是在主函数中先设计功能,再设计子函数,如一个煮饭程序:
main()
{
洗米();
下锅();
打开电饭煲();
}
将中文翻译成英文后,再在main函数顶部分别再设计各子函数的细节:
void 洗米()
{
……
}
另一种是由下而上:许多通用函数设计出来时根本就不知道哪个父函数谁会调用它,比如我们看到的sqrt、printf和各种库函数,程序员将它设计好后把函数的接口(声明部分)交给调用者,调用者不需要知道里面的程序细节,只需按照它的接口调用即可达到目的。
有些函数设计出来,有可能永远都不会被调用。这就好像if语句的分支一样,有些分支因为数据值没有满足条件,就永远都不会执行那个模块。这也是对程序设计资源的一种浪废。