函数调用
函数调用一般有两种方式,一种是形参不会影响实参的传值调用,另一种是形参会影响实参的传址调用。
传值调用:在函数调用过程中,我们将函数值传递进函数体中的时候,实际在函数体内形成了一份新的内存空间用来存储我们传递进去的值,因此形参在函数体内无论再怎么改变都不会影响调用函数时使用的实参。
#define _CRT_SECURE_NO_WARNINGS //添加头文件 #include <stdio.h> #include <stdlib.h> void exchange(int num1, int num2) { int t = num1; num1 = num2; num2 = t; } //主函数,函数入口 int main() { int num1 = 1; int num2 = 2; exchange(num1, num2); printf("num1 = %d,num2 = %d\n", num1, num2); return 0; }
传址调用:传址调用是将参数的地址进行传入,其实就是把指针变量作为参数,而这个指针变量中存放的就是要传入参数的地址,所以有了这个指针变量,我们就可以找到参数的本体,并对其进行操作,所以当我们将变量的地址传入的时候,实参就被锁定了,形参的改变,也会使实参改变。
#define _CRT_SECURE_NO_WARNINGS //添加头文件 #include <stdio.h> #include <stdlib.h> void exchange(int* num1, int* num2) { int t = *num1; *num1 = *num2; *num2 = t; } //主函数,函数入口 int main() { int num1 = 1; int num2 = 2; exchange(&num1, &num2); printf("num1 = %d,num2 = %d\n", num1, num2); return 0; }
嵌套调用:嵌套调用是构成C语言最基础的语法,简单来说就是允许在函数内调用其它函数,就像我们在main()函数中调用外部我们自己写的函数,这就是嵌套调用。
链式访问:链式访问是在函数参数里调用函数,这种调用方式也很简单,不过是将一个有返回值的函数在另一个函数的参数列表中进行调用,运行时会优先调用参数列表中的函数然后根据返回值进行判断外函数如何运行。
递归函数:递归函数简单来说就是函数自己调用自己,形成循环,然后在一定条件下,结束循环,完成递归。
递归函数的思想十分巧妙,内部执行的过程很复杂,因此十分难理解。递归就是将大问题分解为子问题,将最终的子问题的解作为终止条件,而这些子问题和大问题的解法都是一样的,只需要思考大问题和子问题之间的关系就行,不需要一层层往下去思考子问题与子子问题之间的关系。
给出几个例子:
#define _CRT_SECURE_NO_WARNINGS //添加头文件 #include <stdio.h> #include <stdlib.h> //递归求阶乘 int factor(int num) { //如果数字等于一则返回它本身 if (num == 1) { return 1; } return num * factor(num - 1);//num! = num * (num - 1)! } //主函数,函数入口 int main() { printf("8的阶乘是:%d!\n", factor(8)); return 0; }
还有两个详见我的另两篇博
https://www.cnblogs.com/zhm521/p/13888179.html
https://www.cnblogs.com/zhm521/p/13884267.html
函数的声明和定义
函数声明:我们在写代码的时候经常把自定义函数写在main()函数的上方,这样会使代码的可读性变低。别人在看你写的代码的时候,必须得翻看你写的那些杂乱无章的函数,然后才能找到main()函数,这样就很不好。
那么我们可不可以把自定义函数写在main()函数的下方?当然是可以的,只不过我们需要先声明一下,声明要告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但是具体函数存在不存在,无关紧要。
#define _CRT_SECURE_NO_WARNINGS //添加头文件 #include <stdio.h> #include <stdlib.h> //函数的声明可以省略写参数名 int func(int); //主函数,函数入口 int main() { int num = 0; func(num); return 0; } int func(int num) { /* something; */ return num; }
在本例中,函数的定义放在了调用之后,而编译器并没有报错,这就是上方的声明起到了作用。
头文件:我们一般习惯添加一个头文件,然后把函数的声明放进头文件中,在写代码的时候,只要包含这个头文件即可,这样使自己的代码看起来更简洁,可读性更高。
并且头文件的存在,可以使我们将代码分开来写,以后工作中,一个工程可能需要写几百万行代码,如果全都放在一起,那么可读性几乎为零,这时候,我们需要将主函数存放于一个文件,其他的函数分为不同的类别、不同的用法等等,放在其他的文件中,而所有的函数声明放在头文件中,在主函数所在的文件中,包含头文件即可。展示如下:
//头文件 #pragma once int func(int);//函数的声明可以省略写参数名
//另一个.c文件 int func(int num) { /* something; */ return num; }
//主函数所在的文件 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h>//添加头文件 #include <stdlib.h> #include "test.h" int main()//主函数,函数入口 { int num = 0; func(num); return 0; }
注:①自己写的头文件,最好使用“”来包含,因为<>包含的头文件是在库函数中去查找,而“”包含的头文件,则是在本工程中去查找,这样可以提高查找头文件的效率。
②在自己写的头文件上方,我们可以看到这个“#program once”语句,他存在的作用就是保证我们在多次调用同一个我文件的时候不会造成多次调用,发生错误,可以说是为了弥补C语言头文件使用的弊端,也是头文件必须的。