C语言 初识函数

目录:

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

        

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值