目录:
1.函数是什么?
2.函数有库函数,也有自定义函数
3.函数调用
4.函数的嵌套调用,如在函数A中调用B,注意是调用,不是定义
5.函数的链式访问,指函数的返回值作为函数的实参
6.函数的定义与声明
7.函数递归
1.函数是什么?
函数其实就是子程序,是一个具有特定功能的代码块
2.函数有库函数,也有自定义函数
函数存在形参,实参,(可以是内存,变量,表达式,甚至是函数)
形式参数,只在调用的时候分配内存,而且一旦函数结束内存立马销毁,因此称为形式参数
main函数里面调用函数时的是实参,main函数上面定义函数时使用的是形参。
注意:当函数调用的时候,实参传给形参时,形参其实只是实参的一份临时拷贝(另外的内存空间),对形参修改数值不会影响实参的值(当然return回去另当别论)
举例:写一个交换a,b变量的函数,先来错误示范,
#include<stdio.h>
void 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=%d\n", a, b);
exchange(a, b);
printf("交换后:a=%d,b=%d\n", a, b);
return 0;
}
我们可以看到代码结果是有问题的,这是为什么?
原因就是实参a,b传到形参时,电脑为形参x,y创建了新的内存空间,x,y的互换只是另一片存储空间的值的改变,结果并未影响到实参所在的内存地址。
那么我们应该怎么利用形参影响实参呢?
3.函数调用
函数调用分两种:传值调用和传址调用
上面用的就是传值调用,把实参的值调给形参(形参不会影响实参)
那不得不提另一种方法了,传地址给函数,然后函数接受对应的地址后解引用隔空操作另一边的变量
#include<stdio.h>
void 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=%d\n", a, b);
//exchange1(a, b);//传值调用
exchange2(&a, &b);//传址调用
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
ps:数组在传递到函数的时候不是传递一串地址,而是首个元素的地址,
#include<stdio.h>
int binary_search(int arr[], int k)
{
//代码就不写全了,水平有限
int sz = sizeof(arr) / sizeof(arr[0]);//这里的sizeof(arr)其实是sizeof(*int),所以想
利用这个操作求arr数组的长度是不现实的,最后sz是1
}
int main()
{
int ret = binary_search(arr, k);
return 0;
}
总结:函数要尽可能简洁(不是简单!)明了,功能单一,不要杂七杂八全往里塞,
在不需要改变实参的值是可以用传值调用;需要改变实参时考虑传址调用。
---------------------------------------------------------------------------------------------------------------------------------
4.函数的嵌套调用
如在函数A中调用B,注意是调用,不是定义
C语言支持函数嵌套调用,但不支持嵌套定义
5.函数的链式访问,指函数的返回值作为函数的实参
如printf("%d\n", printf("%d\n", printf("%d\n",123))); 就是链式调用
printf函数的返回值是printf实际控制输出的字符数,如第一次是123\n四个字符,因此第一次先打印123,返回4;
第二次是4\n两个字符,所以返回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