函数(二)+递归

文章介绍了函数的声明和定义在编程中的重要性,通过示例解释了如何使用它们。递归的概念被详细阐述,包括递归条件和栈溢出问题。文中还讨论了递归在解决斐波那契数列等问题时的效率问题,并提出了迭代实现作为优化方案。递归在提高代码可读性方面的价值被提及,但也指出其可能的效率劣势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于一个函数来说,函数的声明是必不可少的。在我们使用一个函数之前,必须先对这个函数进行定义或者声明。

举一个简单的例子:

函数的定义

函数的定义是指函数的具体实现,并交待函数的功能实现。

我们写了一个简单的函数,并在其中引入了一个自定义的add函数,这是我们直接运行主函数的话,编译器是无法检测到add函数的存在,为了解决这个问题,我们可以进行一个对函数的定义。

有了函数定义以后,编译器在检测到函数定义后便可以直接调用add函数。

函数的声明

如果我们不想让函数的定义放在主函数之前,那么还有一种办法就是函数的声明。

1. 函数声明会告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数 声明决定不了。 2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。 3. 函数的声明一般要放在头文件中的。

以刚才的代码为例,如果我们想要add函数放在主函数后面,我们可以写成如下代码:

这两种办法可以轻松使用自定义函数,但是在日常生活中,我们更多的是将函数分模块来写,并且封装成静态库。这样会大大提升团队协作效率,使代码变得更加简洁清晰。

函数的递归

程序调用自身的编程技巧称为递归( recursion)。 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接 调用自身的 一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解, 递归策略 只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

递归的条件

  1. 存在限制条件,当递归到达这个限制条件时便停止递归。

  1. 每次递归调用之后便越来越接近这个条件。

递归实例

我们首先举一个最简单的例子来了解递归。

在main函数中,我们又一次调用main函数,这就叫做递归,但是当我们运行这个程序的时候,我们会发现一个问题

Stack overflow是指栈溢出,因为我们并没有设定递归条件,而递归每次会在栈内开辟一片新的存储空间,当递归函数连续不断的调用自己的时候,栈空间就会饱和从而发生栈溢出的错误。

接下来我们再来看一个有限定条件的例子:接受一个整型数值并打印出每一位数字。

可以先思考一下,如果我们要打印各个位的数字,可以使用除以10和取余操作来处理。例如我们要打印1234四位,第一位1是最容易打印的,可以将其化为1(234),剩余的234中2是最好打印的,可以转化为12(34),以此类推,我们可以得到如下代码:

运行之后可得结果

我们来分析一下过程,当我们输入1234时,n将1234传给print函数。开始调用print函数并判断n>9,于是将n除以10再次调入print函数,此时的n为123,以此类推,最后的n<9,于是直接模10输出,输出后跳转进倒数第二个的模10部分输出,以此类推,便得到了1 2 3 4四个数字。

我们再讲解一个经典例子:求斐波那契数列。

参考代码:

但我们在运行时发现了问题:

在使用 fib 这个函数的时候如果我们要计算第50个斐波那契数字的时候特别耗费时间。 使用 factorial 函数求10000的阶乘(不考虑结果的正确性),程序会崩溃。 为什么呢? 我们发现 fib 函数在调用的过程中很多计算其实在一直重复。

此时的办法是将递归调用改为非递归调用:

这样我们再次输出就是一个非常大的值,此处不在进行演示。

结论

1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。 2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。 3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开 销。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值