1.函数是什么?
函数其实就是子程序,是一个具有特定功能的代码块
2.函数有库函数,也有自定义函数
函数存在形参,实参,(可以是内存,变量,表达式,甚至是函数)
形式参数,只在调用的时候分配内存,而且一旦函数结束内存立马销毁,因此称为形式参数
main函数里面调用函数时的是实参,main函数上面定义函数时使用的是形参。
注意:当函数调用的时候,实参传给形参时,形参其实只是实参的一份临时拷贝(另外的内存空间),对形参修改数值不会影响实参的值(当然return回去另当别论)
举例:写一个交换a,b变量的函数,先来错误示范,
#includevoid exchange(int x, int y) { int z = 0; z = x; x = y; y = z; } int main() { int a, b; scanf("%d %d", &a, &b); printf("交换前:a=%d,b=%dn", a, b); exchange(a, b); printf("交换后:a=%d,b=%dn", a, b); return 0; }
我们可以看到代码结果是有问题的,这是为什么?
原因就是实参a,b传到形参时,电脑为形参x,y创建了新的内存空间,x,y的互换只是另一片存储空间的值的改变,结果并未影响到实参所在的内存地址。
那么我们应该怎么利用形参影响实参呢?
3.函数调用
函数调用分两种:传值调用和传址调用
上面用的就是传值调用,把实参的值调给形参(形参不会影响实参)
那不得不提另一种方法了,传地址给函数,然后函数接受对应的地址后解引用隔空操作另一边的变量
#includevoid exchange2(int* pa, int* pb)//拿a,b的地址 { int z = 0; z = *pa; *pa = *pb;//解引用操作,对*pa操作就是对变量a操作 *pb = z; } int main() { int a, b; scanf("%d %d", &a, &b); //交换2个变量 printf("交换前:a=%d b=%dn", a, b); //exchange1(a, b);//传值调用 exchange2(&a, &b);//传址调用 printf("交换后:a=%d b=%dn", a, b); return 0; }
ps:数组在传递到函数的时候不是传递一串地址,而是首个元素的地址,
#includeint binary_search(int arr[], int k) { //代码就不写全了,水平有限 int sz = sizeof(arr) / sizeof(arr[0]);//这里形参其实取到了实参数组的首个元素的地址,所以想 利用这个操作求arr数组的长度是不现实的,最后sz是1 } int main() { int ret = binary_search(arr, k); return 0; }
总结:函数要尽可能简洁(不是简单!)明了,功能单一,不要杂七杂八全往里塞,
在不需要改变实参的值是可以用传值调用;需要改变实参时考虑传址调用。
---------------------------------------------------------------------------------------------------------------------------------
4.函数的嵌套调用,如在函数A中调用B,注意是调用,不是定义
C语言支持函数嵌套调用,但不支持嵌套定义
5.函数的链式访问,指函数的返回值作为函数的实参
如printf("%dn", printf("%dn", printf("%dn",123))); 就是链式调用
printf函数的返回值是printf实际控制输出的字符数,如第一次是123n四个字符,因此第一次先打印123,返回4;
第二次是4n两个字符,所以返回2
6.函数的定义与声明 (ps:建议先写调用函数然后再回到mian函数上面定义)【先想好怎么用在定义不容易错】--仅代表个人观点
函数一般有两种使用方式 第一种先声明,然后再main函数里调用,最后定义函数(大部分经典计算机书上都这么写,感觉麻烦)
第二种 在main函数前定义,然后使用
【函数建议写在头文件里,然后在源文件里引用】
7.函数递归,
1.就是函数调用的过程中调用了自身,从而将一个复杂的问题一次次转化为相对简单的同类型问题。递归,先递后归,把问题一步步递到简单的问题,然后回归
如:想知道5个人的年龄,a说他一岁,b说比a大一岁,c说比b大一岁以此类推,
我们想知道e的年龄就要知道d的,然后就要知道c的····直到直到a的,这就是递的过程;
知道了a的年龄,我们就可以知道b的,然后知道c的,一步步归回e的年龄
2.函数递归有两个必要条件(没有程序会死递归出错)
一个是设置递归的结束条件,还是上面的例子,想知道的e的年年龄我们只需要递到a就可以了,a的年龄为1岁就是结束条件;
还有一个是递归的关系式(能让问题不断趋于简单的方向,如果不能那还用什么递归)
3.函数递归的stackoverflow(栈溢出)【为什么死递归会导致程序崩溃】
内存分为三块,函数调用时程序会在栈区请求开辟一块空间,每一次调用函数函数都会开辟一片空间,如果是死循环那么当栈区没有空间可用时编译器就会报stackoverflow。
ps:stackoverflow也是国外一个网站,相当于程序员的知乎,不过是英文的
Stack Overflow - Where Developers Learn, Share, & Build Careers



