【函数详解(下)】||[函数声明和定义+函数递归]重点剖析+代码图解

 

 

目录

函数的声明和定义

工具说明书——函数声明

制作工具——函数定义

分模块写函数的优点

 函数递归

递归的两个必要条件

练习1:接受一个整型值(无符号),按照顺序打印它的每一位。

练习2: 编写函数不允许创建临时变量,求字符串的长度。

 练习3: 求n的阶乘。(不考虑溢出)

练习4: 求第n个斐波那契数。

递归函数的致命缺陷:巨大的时间开销和内存开销

改进优化


函数的声明和定义

函数的声明与函数的定义形式上十分相似,但是二者有着本质上的不同。声明是不开辟内存的,仅仅告诉编译器,要声明的部分存在,要预留一点空间。定义则需要开辟内存。

工具说明书——函数声明:

C语言代码由上到下依次执行,原则上函数定义要出现在函数调用之前,正如变量一样,我需要先定义一个变量,再使用变量,否则就会报错。但在实际开发中,经常会在函数定义之前使用它们,这个时候就需要提前声明。

1.函数声明就是要告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但具体是否存在,无关紧要,但请编译器大爷不要报错哦,我稍后把定义补上。

2.函数声明一般出现在函数调用之前,要满足先声明后使用

3.函数声明一般放在头文件里。

格式:

返回类型   函数名(参数1类型 参数1,参数2类型 参数2,……);

int Add(int x,int y);

制作工具——函数定义:

如果把函数声明比作“工具的说明书”,那函数定义则相当于制作这个工具。函数定义就是函数的具体实现,交代函数的功能实现。在程序中,函数的定义只能有一次

格式:

 返回类型   函数名(参数1类型 参数1,参数2类型 参数2,……)
 {
      函数体······
 }

int Add(int x,int y)
{
    return x+y;
}


 我们在教科书中,实现一个函数如下图所示:

 但我们在实现工程时,实现一个函数是分多模块写:

1.我们先分别创建一个源文件和头文件,分别命名为Add.c和Add.h

2.将函数声明放入头文件Add.h中,将函数定义放入源文件Add.c中。

3.Add.c和Add.h合起来是一个加法模块,包括函数声明和函数定义,未来在使用加法函数时,只要#include"Add.h"即可。

  

程序的生成先是把每个源文件编译成.obj,然后再把几个.obj链接生成程序。
一个源文件里只要声明了函数,就可以正常编译成.obj,链接的时候才会去绑定函数定义。
所以你可以在源文件test.c里通过##include"Add.h"声明一个函数,在Add.c里定义(实现)它。 这样,test.c和Add.c就关联起来了。如图所示:

分模块写函数的优点:

1.可以多人协作,分工明确,高效率。

2.可以实现封装和隐藏

比如说A公司开发出了一个复杂函数,可以售卖给其他公司供其使用,但是A公司又不想把全部的函数具体实现(Add.c)售卖出去,该公司只想把这个功能首卖出去,实现按年收费。于是就可是对函数实行封装和隐藏。

例如A公司开发出一个Add函数

 A公司只把Add.h和Add.lib卖给其他公司

 

 


 函数递归

一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层,当最内层的函数执行完毕后,再一层一层地由里到外退出。

它通常把一个大型复 杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可 描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在 于:把大事化小。

递归的两个必要条件

1.存在限制条件,当满足这个限制条件的时候,递归便不再继续。

2.每次递归调用之后越来越接近这个限制条件

练习1:接受一个整型值(无符号),按照顺序打印它的每一位。

Print(unsigned int num)
{
	if (num > 9)
	{
		Print(num / 10);
	}
	printf("%d ", num % 10);
}
int main()
{
	unsigned int num = 1234;
	Print(num);
	return 0;
}

 

练习2: 编写函数不允许创建临时变量,求字符串的长度。

int my_strlen(char *p)
{
	if (*p != '\0')
	{
		return 1 + my_strlen(p + 1);
	}
	else return 0;	
}
int main()
{
	char arr[] = "abcd";
	
	int len=my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

 练习3: 求n的阶乘。(不考虑溢出)

int fac(int n)
{
	if (n == 1 || n == 0)
	{
		return 1;
	}
	else
		return fac(n - 1)*n;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	fac(n);
	return 0;
}

练习4: 求第n个斐波那契数。

int fib(int n)
{
	if (n <= 2)
	{
		return 1;
	}
	else
	{
		return fib(n - 2) + fib(n - 1);
	}
}
	int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret=fib(n);
	printf("%d\n",ret);
	return 0;
}

 

 但是我们发现有问题;

1.在使用 fib 这个函数的时候如果我们要计算第50个斐波那契数字的时候特别耗费时间。

2.使用 fac 函数求10000的阶乘(不考虑结果的正确性),程序会崩溃。

递归函数的致命缺陷:巨大的时间开销和内存开销

 

 

 

改进优化:

int fib(int n)
{
	int a = 1;
	int b = 1;
	int c = 1;
	while (n > 2)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = fib(n);
	printf("%d\n", ret);

	return 0;
}
int fib(int n)
{
	int a = 1;
	int b = 1;
	int c = 1;
	while (n > 2)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = fib(n);
	printf("%d\n", ret);

	return 0;
}

提示:

1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。

2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。

3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开 销。


完结。 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值