函数基础与递归简介

下面先给出实例:

int Add(int x, int y)
{
	return 0;
}
返回数据类型  函数名 (参数接收)
{
	return 返回值;
}

——int为返回数据类型,一般可为char,int,float...常见类型。

——函数名可自行决定(不能为关键字),后可用于函数调用。

——参数接收即为形参设置,接收函数调用时传递的实参数据。

——return 返回值,即函数调用完成后返回的值

需注意,void(即为空类型)类型无返回值

函数能将具有独立功能的代码封装成一个函数,假如在项目中我们需要频繁使用一段代码的功能,只需将其封装成函数,往后每次使用只需通过函数调用进行使用即可。函数封装提高了代码的复用性,并且分模块写代码也提高了代码的可读性,试想一下假如没有函数,写一个程序上万行代码全都在主函数内,调试时那就是折磨了。

函数一旦创建,如果不进行调用,则不会为其分配内存空间。一旦进行调用,才会在栈上开辟空间,创建变量。

函数定义和声明

函数调用时若函数定义写在函数调用前,则可不进行函数声明。否则若函数定义写在调用之后,则必须在之前进行函数声明。下面为实例:

int add(int x, int y);//函数声明

int main()
{
	int x = 10;
	int y = 20;
	printf("%d", add(x,y));
	return 0;
}
int add(int x, int y)
{
	return x + y;//函数定义
}

函数声明只需对返回类型,函数名,接收参数部分声明即可,具体需执行代码则放在函数定义内。

从代码中还可看出,函数内形参是可以与外部形参变量同名的。

函数的嵌套调用和链式访问:

嵌套调用,即在一个函数内部调用其他函数

int add(int x, int y)
{
	return x + y;
}
void test(int x,int y)
{
	printf("%d", add(x, y));//嵌套调用
}
int main()
{
	int x = 10;
	int y = 20;
	test(x, y);
	return 0;
}

链式访问,即把一个函数的返回值作为实参传递给另一个函数

int add(int x, int y)
{
	return x + y;
}
void test(int x)
{
	printf("%d", x);//嵌套调用
}
int main()
{
	int x = 10;
	int y = 20;
	test(add(x,y));//链式访问
	return 0;
}

函数的传值调用和传值调用:

int test(int x)
{
	x = 1;
	return 0;
}
int main()
{
	int x = 10;
	test(x);
	printf("%d", x);
	return 0;
}

虽然函数内外都有名为x的变量,但是在函数内部的x为函数形参,在其内对形参x的改变并不会影响实参,因此打印x的原值10。

当然如果x为全局变量函数内部自然可以对其进行修改。

那如何通过函数修改main函数内部变量呢?

此处需要传值调用

int test(int* x)
{
	*x = 1;
	return 0;
}
int main()
{
	int x = 10;
	test(&x);
	printf("%d", x);
	return 0;
}

此处需要引入指针的概念,指针变量可以指向变量所在内存,因此只需传递外部变量地址的值,在函数内部即可通过地址修改外部变量,类似于一个遥控器。指针变量可将函数内部与外部变量联系起来。

函数递归

简单来说就是函数自身调用自身

int main()
{
	printf("hello world\n");
	main();
	return 0;
}

这是最简单的一个递归(虽然并不正确),其中第一个main函数调用自身,然后又进入第二个main(),继续调用自身.......函数递归便形成了"死循环",实际上,执行不就后,代码就会因为"栈溢出"而崩溃。那么为什么这里要形容成第一,第二个main函数呢?不应该是自身调用自身吗?

这样做是为了将其与循环区别开。要搞清楚这些内容,首先要明白函数在栈上存储,因此我们要先介绍执行函数时函数在上的情况。

 这是函数入栈与出栈,首先执行第1个main函数,直到调用自身,进入第2个main函数,注意!此时第1个main函数并未结束,而栈上又为第2个main函数分配了空间,直到再次调用自身,进入第3个main函数,此时第2个main函数也未结束.......

直到如果函数停止调用自身,开始出栈。假设调用到第6个函数时结束调用,则第6个函数出栈,然后继续往后执行第5个函数,直到第5个函数出栈,然后继续往后执行第4个函数.......

递归是很多算法都使用的一种编程方法,能用极其简洁的代码完成复杂的工作,只需将问题分解为基线条件和递归条件。满足递归条件时,函数调用自身,直到满足基线条件时函数结束调用,开始返回,逐个出栈。 若无基线条件,则函数无限递归,必然导致栈溢出,因此使用递归一般必须有基线条件和递归条件。

现给出用函数递归求n!的代码:

int Fac(int n)
{
	if (n == 1)
		return 1;
	else
		return n * Fac(n - 1);
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	printf("%d",Fac(n));
	return 0;
}

函数递归求解问题的思路便是将这个问题转化成更小规模的问题,比如将n!转化成n*(n-1)!,则(n-1)!可继续细分为 (n-1)*(n-2)!,直到基线条件。因此递归返回n*Fac(n-1)即可,直到基线条件时返回1,即可通过不断的自身调用实现阶乘。

函数递归是一种简洁优雅的算法,然而其在运行效率上并不算高效

int Fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return Fib(n-1)+Fib(n-2);
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	printf("%d",Fib(n));
	return 0;
}

以上为斐波那契数列第n项的递归实现,然而当输入项数较大时,程序便难以得出结果。这是因为此时进行了过多次的重复调用。

可以看出,在求第40项,仅第3项就调用了近4kw次

综上,我们应尽综合考虑代码的简洁与复杂度,函数递归基础知识到此结束

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值