【C语言入门】尾递归优化

全面理解-什么是尾递归优化? 尾递归(Tail Recursion) 是一种特殊的递归形式,其特点是递归调用是函数的 最后一步操作。尾递归可以被编译器优化为迭代形式,从而避免递归调用带来的栈溢出问题,并提升性能。 阅读详情

1. 递归:程序世界的 “套娃游戏”

递归(Recursion)是计算机科学中最经典的算法思想之一,其核心是 “函数调用自身”。从数学归纳法到分治算法,从二叉树遍历到动态规划,递归的身影无处不在。但递归也有一个致命问题:栈溢出(Stack Overflow)。

1.1 递归的执行机制:栈帧的 “叠罗汉”

要理解递归的问题,首先需要理解程序运行时的 “栈空间”(Stack)。当一个函数被调用时,计算机会为它分配一块内存区域,称为 “栈帧”(Stack Frame),用于存储:

  • 函数的参数和局部变量;
  • 调用该函数的 “返回地址”(即上一层函数执行完当前函数后,下一步要执行的指令位置);
  • 其他需要保存的状态(如寄存器值)。

以计算阶乘的递归函数为例(C 语言):

int factorial(int n) {
    if (n == 0) return 1;        // 基线条件(Base Case)
    return n * factorial(n - 1); // 递归调用(Recursive Case)
}

当调用 factorial(5) 时,程序的执行过程如下:

  1. 调用 factorial(5),创建栈帧 A,保存 n=5,返回地址(假设为位置 X);
  2. 执行到 return 5 * factorial(4),调用 factorial(4),创建栈帧 B,保存 n=4,返回地址(位置 Y);
  3. 同理,依次创建栈帧 C(n=3)、D(n=2)、E(n=1)、F(n=0);
  4. 当 n=0 时,返回 1,此时需要回溯计算:
    • 栈帧 E:1 * 1 = 1(返回给 D);
    • 栈帧 D:2 * 1 = 2(返回给 C);
    • …… 直到栈帧 A:5 * 24 = 120。

整个过程中,栈帧的数量等于递归深度(此处为 5)。如果计算 factorial(10000),栈帧数量会达到 10000 层,而计算机的栈空间通常只有几 MB(例如,Linux 默认栈大小为 8MB),每层栈帧至少占用几十字节,最终会因 “栈帧叠罗汉” 超过栈空间上限,导致程序崩溃(栈溢出错误)。

1.2 递归的 “阿克琉斯之踵”:栈深度与问题规模的矛盾

递归的简洁性让它成为算法设计的 “利器”,但它的栈空间复杂度为 O (n)(n 为递归深度)。对于大规模问题(如计算 10 万阶乘、遍历百万节点的链表),普通递归会因栈溢出直接失效。此时,尾递归优化(Tail Recursion Optimization, TRO)成为解决这一问题的关键。


2. 尾递归:递归的 “自洽终点”

尾递归是递归的一种特殊形式,其核心特征是:递归调用是函数的最后一个操作(即 “尾调用”)。换句话说,函数在执行完递归调用后,不需要再执行任何其他操作(如计算、返回值合并等)。

2.1 尾递归的形式定义

数学上,尾递归函数满足以下条件:

  • 函数存在一个 “累积器”(Accumulator)参数,用于保存中间结果;
  • 递归调用是函数的最后一条语句;
  • 基线条件(Base Case)返回累积器的值。

以尾递归实现的阶乘函数为例:

int factorial_tail(int n, int acc) {
    if (n == 0) return acc;          // 基线条件:返回累积结果
    return factorial_tail(n - 1, n * acc); // 尾递归调用:最后一步仅调用自身
}

// 包装函数,隐藏累积器参数
int factorial(int n) {
    return factorial_tail(n, 1);
}

对比普通递归,尾递归的关键变化是引入了累积器 acc。在每一步递归中,acc 保存当前的阶乘中间结果(如计算 5! 时,acc 依次为 1→1→2→6→24→120)。当递归到达基线条件(n=0)时,直接返回 acc,无需回溯计算。

2.2 尾递归与普通递归的本质区别:栈帧的 “生存周期”

普通递归的栈帧需要 “等待” 子调用返回后,才能计算最终结果(如 n * factorial(n-1) 必须等 factorial(n-1) 返回后,才能计算乘法)。因此,每一层栈帧在子调用结束前不能被释放,必须保留到整个递归链完成。

而尾递归的栈帧在调用自身后,不再需要任何后续操作(因为结果已经通过累积器传递给下一层)。因此,编译器可以优化:将当前栈帧直接 “替换” 为下一层递归的栈帧,而不是新建一个栈帧。这种优化称为 “栈帧复用”(Stack Frame Reuse),它将递归的栈空间复杂度从 O (n) 降为 O (1)(仅需保留一层栈帧)。


3. 尾递归优化的技术原理:编译器的 “魔法”

尾递归优化不是语言特性,而是编译器提供的 “优化手段”。只有当编译器支持尾调用优化(Tail Call Optimization, TCO)时,尾递归才能避免栈溢出。不同语言对 TCO 的支持程度不同(如 Scheme 强制要求支持,C/C++ 依赖编译器实现,Python 默认不支持)。

3.1 编译器如何识别尾调用?

要触发尾递归优化,递归调用必须满足以下条件:

  1. 调用是函数的最后一条语句:递归调用后不能有任何操作(如计算、赋值、返回值处理等);
  2. 调用结果直接作为当前函数的返回值:即 return f(...);,而不是 return g(f(...)); 或 int x = f(...); return x;;
  3. 调用的是当前函数自身(对尾递归而言):如果是尾调用其他函数(如 return g(x);),则属于更广义的尾调用优化,但本文聚焦尾递归。

以 C 语言为例,GCC 编译器通过分析函数的控制流,判断递归调用是否满足 “尾调用” 条件。如果满足,GCC 会修改函数的汇编代码,将递归调用转换为 “跳转”(Jump)而非 “调用”(Call)。

3.2 从汇编看优化:Call vs. Jump

普通递归的汇编(简化版):

factorial:
    push %rbp          ; 保存旧栈基址
    mov %rsp, %rbp     ; 设置新栈基址
    cmp $0, %rdi       ; 检查n是否为0
    je .base_case
    sub $8, %rsp       ; 为局部变量分配空间
    mov %rdi, -8(%rbp) ; 保存n到栈
    dec %rdi           ; n-1
    call factorial     ; 调用自身(压入返回地址到栈)
    imul -8(%rbp), %rax ; 计算n * factorial(n-1)
    leave              ; 恢复栈基址
    ret                ; 返回结果
.base_case:
    mov $1, %rax       ; 返回1
    leave
    ret

尾递归优化后的汇编(GCC 开启 -O2 优化):

factorial_tail:
    .LBB0_1:
    cmp $0, %rdi       ; 检查n是否为0
    je .base_case
    imul %rdi, %rsi    ; 计算n * acc(累积器更新)
    dec %rdi           ; n-1
    jmp .LBB0_1        ; 跳转到自身(不压入返回地址)
.base_case:
    mov %rsi, %rax     ; 返回acc
    ret

关键区别在于:普通递归使用 call 指令(会压入返回地址,增加栈深度),而尾递归优化后使用 jmp 指令(直接跳转到函数起始位置,不增加栈深度)。这相当于把 “递归” 变成了 “循环”,因此尾递归优化也被称为 “用递归语法实现循环”。

3.3 优化的限制:并非所有尾递归都能被优化

尽管尾递归优化很强大,但它受限于编译器实现和语言特性:

  • 语言限制:C/C++ 等命令式语言的编译器(如 GCC)通常只对显式的尾递归进行优化,而函数式语言(如 Scheme)则强制要求支持尾调用优化;
  • 调用约定限制:如果函数使用了可变参数(如 printf)、需要保存非易失性寄存器(如 x86 的 %rbp),或涉及异常处理(如 C++ 的 try/catch),编译器可能无法优化;
  • 栈帧布局限制:如果函数的栈帧中包含局部变量(尤其是大数组),即使满足尾调用条件,优化后的栈帧复用可能导致数据覆盖,因此编译器可能选择不优化。

4. 尾递归的实践:从理论到代码
4.1 如何编写可优化的尾递归函数?

编写尾递归函数的关键是引入累积器参数,将中间结果通过参数传递,避免依赖上一层栈帧的状态。以下是几个经典示例:

示例 1:计算斐波那契数列
普通递归(O (n) 栈空间):

int fib(int n) {
    if (n <= 1) return n;
    return fib(n - 1) + fib(n - 2); // 非尾递归:需要合并两个子调用的结果
}

尾递归优化版本(O (1) 栈空间):

int fib_tail(int n, int a, int b) {
    if (n == 0) return a;
    return fib_tail(n - 1, b, a + b); // 尾递归:最后一步仅调用自身
}

int fib(int n) {
    return fib_tail(n, 0, 1); // 初始累积器a=0(F(0)),b=1(F(1))
}

示例 2:反转字符串
普通递归(O (n) 栈空间):

void reverse_str(char *str, int start, int end) {
    if (start >= end) return;
    char temp = str[start];
    str[start] = str[end];
    str[end] = temp;
    reverse_str(str, start + 1, end - 1); // 尾递归?不,因为交换操作在递归调用前
}

尾递归优化版本(O (1) 栈空间):

void reverse_str_tail(char *str, int start, int end) {
    if (start >= end) return;
    // 交换字符
    char temp = str[start];
    str[start] = str[end];
    str[end] = temp;
    // 尾递归调用:最后一步仅调用自身
    reverse_str_tail(str, start + 1, end - 1);
}

注意:这个例子中的递归调用是尾调用吗?是的!因为交换操作在递归调用之前完成,递归调用是函数的最后一步。因此,编译器可以优化其栈空间。

4.2 尾递归与循环的关系:殊途同归

从执行效率上看,优化后的尾递归与循环(For/While)几乎等价。例如,尾递归版本的阶乘可以改写为循环:

int factorial_loop(int n) {
    int acc = 1;
    while (n > 0) {
        acc *= n;
        n--;
    }
    return acc;
}

两者的核心都是通过 “累积器 + 迭代” 避免栈增长。尾递归的优势在于保持了递归的 “声明式” 风格(描述 “做什么” 而非 “怎么做”),而循环更偏向 “命令式”(描述 “如何做”)。对于复杂的递归逻辑(如树遍历、分治算法),尾递归的可读性往往更高。

4.3 尾递归的局限性:并非万能药

尽管尾递归优化能解决栈溢出问题,但它无法解决所有递归的性能问题:

  • 时间复杂度:尾递归的时间复杂度与普通递归相同(如阶乘的 O (n)),优化仅减少空间复杂度;
  • 适用场景:只有当递归调用是函数的最后一步时才能优化,对于需要合并多个子调用结果的递归(如斐波那契的普通递归),无法直接优化;
  • 语言支持:Python、Java 等语言默认不支持尾调用优化(Python 的递归深度限制约为 1000 层),此时尾递归写法无法避免栈溢出。

5. 尾递归优化的历史与现状

尾递归优化的概念最早由函数式编程语言(如 1975 年的 Scheme)提出。Scheme 的语言规范明确要求编译器必须支持尾调用优化,这使得递归成为 Scheme 的核心编程范式。

在命令式语言中,C/C++ 的编译器(如 GCC、Clang)通过扩展支持尾递归优化(需开启 -O2 或更高优化级别)。例如,GCC 的 -foptimize-sibling-calls 选项可启用尾调用优化。

近年来,随着函数式编程思想的普及,越来越多的语言开始支持尾调用优化:

  • JavaScript:ES6(ECMAScript 2015)规范要求支持尾调用优化,但主流浏览器(如 Chrome、Firefox)仅部分实现;
  • Scala:默认支持尾递归优化(需用 @annotation.tailrec 注解标记);
  • Rust:通过 LLVM 后端支持尾调用优化;
  • C#:在.NET Core 3.0 + 中,通过 RyuJIT 编译器支持尾调用优化。

6. 总结:尾递归优化的 “道” 与 “术”

尾递归优化是编译器对递归的 “善意” 优化,它通过栈帧复用将递归的空间复杂度从 O (n) 降为 O (1),解决了大规模递归的栈溢出问题。理解尾递归的关键在于抓住两个核心:

  • 形式:递归调用是函数的最后一步;
  • 本质:通过累积器传递中间结果,避免依赖上一层栈帧。

对于开发者而言,掌握尾递归优化不仅能写出更健壮的递归代码,还能深入理解程序运行时的栈机制。尽管尾递归无法替代所有递归场景,但其思想(用迭代的空间效率实现递归的简洁性)对算法设计和性能优化具有重要启示。

形象解释:用 “爬楼梯” 理解尾递归优化

想象一个生活场景:你要爬 100 层楼梯,每爬一层要记录当前的层数和目标。假设你是个 “健忘症患者”,每次爬楼时只能记住两件事:当前在哪一层,还剩多少层要爬。

如果用普通递归的方式爬楼,每爬一层,你都要在脑子里新建一个 “小本子”,写上:“我现在在第 n 层,还剩(100-n)层”。当爬到第 100 层时,你已经攒了 100 个小本子 —— 这时候如果突然有个 “意外”(比如记错了层数),你需要从最后一个小本子开始倒推检查,这会占用很多 “脑容量”(对应程序的 “栈空间”)。如果楼梯特别高(比如 10000 层),你的脑子会被小本子撑爆(栈溢出)。

而尾递归优化就像有个 “贴心的助手”。当你爬完第 n 层后,助手会帮你把 “小本子” 扔掉,只保留最新的两个信息:“当前在第 n+1 层,还剩(100-(n+1))层”。这样不管爬多少层,你脑子里永远只有一个小本子 —— 因为每一步的 “小本子” 都被重复利用了(对应程序的 “栈帧复用”)。

关键区别:普通递归的每一步都要 “攒小本子”(栈空间不断增长),而尾递归的每一步都 “扔掉旧本子,用新本子”(栈空间几乎不增长)。编译器(比如 GCC)会识别这种 “最后一步只调用自己” 的递归,自动开启这个 “助手” 功能,避免栈溢出。

Material Statuses,在 SAP 物料主数据里控制物料能不能用,不只是一个状态字段 SAP物料主数据中的Material Statuses常被低估,实则影响多个业务模块。它通过跨工厂、工厂特定、跨分销链和分销链特定四类状态控制物料生命周期,各状态会联合作用。正确配置状态可精准控制研发、采购、生产、销售等环节的物料使用权限,避免单纯依赖删除标记或权限带来的业务风险。实施时需注意状态编码无固定语义,应结合企业实际业务流程配置,并通过标准接口更新数据。 阅读详情

相关推荐

深入Rust模式优化:尾递归消除与内存布局调整.pdf

文档支持目录章节跳转同时还支持阅读器左侧大纲显示和章节快速定位,文档内容完整、条理清晰。文档内所有文字、图表、函数、目录等元素均显示正常,无任何异常情况,敬请您放心查阅与使用。文档仅供学习参考,请勿用作商业用途。 Rust 以内存安全、零成本抽象和并发高效的特性,重塑编程体验。无需垃圾回收,却能通过所有权与借用检查机制杜绝空指针、数据竞争等隐患。从底层系统开发到 Web 服务构建,从物联网设备到高性能区块链,它凭借出色的性能和可靠性,成为开发者的全能利器。拥抱 Rust,解锁高效、安全编程新境界!

【算法】递归:递归优化之尾递归

文章首先会先通过斐波那契数列来复习在以往介绍的解决递归的方法,然后在通过这个例子去分析递归法的不足之处,最后将会介绍一下关于递归的优化策略,并举例实现。

qq_66725576的博客 4967

ESP8266+ITR8307实战:如何用反射式光电管实现5cm远距离检测(附避坑指南)

本文详细介绍了如何利用ESP8266的ADC功能与ITR8307反射式光电管,通过调制解调、硬件电路优化及软件同步采样策略,实现5cm稳定远距离检测。文章提供了完整的硬件设计、代码实现与调优避坑指南,有效解决了环境光干扰与信号衰减难题,适用于智能车、自动化等需要可靠非接触检测的项目。

rgv234567的博客 264

尾递归优化

廖雪峰python3 摘录+整理

idyllcheung的专栏 2927

光纤接口类型与应用场景全解析

本文全面解析了光纤接口的类型与应用场景,详细介绍了SC、LC、FC和ST等主流光纤接口的特点及适用环境。从数据中心的高密度布线到工业环境的极端条件,提供了专业的接口选型指南和实战经验,帮助读者根据具体需求选择最合适的光纤接口方案。

weixin_29289821的博客 411

直流电动机的建模仿真(1)他励,串励和并励电动机建模仿真

本文根据不同励磁方式的直流电动机的接线原理图,建立了相应的数学模型,然后根据与数学模型对应的等效电路以及模拟机械运动方程的等效电路,用Matlab/simulink中的SpecializedPower system库中的电路元件,建立了他励,并励和串励直流电动机的全等效电路仿真模型,经过与Matlab自带的直流电动机仿真模型比较以及仿真实例验证,所建立的仿真模型时完全正确的。

weixin_45670943的博客 7520

[C编程入门] 第五章:C语言中的函数和递归

函数是C语言中的基本构建块之一,用于封装可重用的代码段。5.1.1 定义和调用函数 语法: 示例: 定义函数:调用函数:5.1.2 函数参数函数可以接受零个或多个参数。 语法: 示例: 函数参数:5.1.3 函数返回值函数可以返回一个值给调用者。 语法: 示例: 函数返回值:5.1.4 函数原型函数原型是在函数定义之前声明函数的一种方法。 语法: 示例: 函数原型:5.1.5 函数重用函数可以被多次调用以执行相同的操作。 示例: 函数重用:C语言中的函

极客代码 979

oh-my-zsh详细安装与主题插件配置

使用Linux的小伙伴都知道默认shell是bash,但是对于喜欢折腾,喜欢酷炫的我们bash怎能够用。zsh的作为一种shell,功能更强大,但是配置复杂。后来,有大佬创建了一个名为oh-my-zsh的开源项目,使用方便,功能强大。 下面就去oh-my-zsh的配置。 ①安装zsh 想要安装oh-my-zsh,首先得安装zsh,输入命令 sudo apt-get install zsh 界面如下 ②查看是否安装成功 输入命令 zsh --version 可以查看安装zsh的版本..

惜洛-Jankin的博客 2万+

【C语言】——尾递归

递归效率太低?那上尾递归!

yusjushd的博客 3215

Navicat16/17数据库密码安全解析与实战解密指南

本文详细解析了Navicat 16/17版本中数据库密码的加密机制,并提供了实用的密码找回指南。通过导出连接文件获取加密密文,利用基于AES-128-CBC算法的本地HTML工具,可安全、离线地解密遗忘的密码。文章强调该方法仅适用于合法恢复自有密码,并给出了操作步骤与安全建议。

weixin_28835583的博客 467

RK3588开发笔记-5G模块(SIM8200、广和通FM160、移远RM500U)调试笔记

RK3588 CPU提供了丰富的外设接口,本文主要介绍通过usb3.0接口驱动5G模块来实现5g无线数据传输功能。在物联网和边缘计算领域,5G网络支持的高速率、低延时连接为应用带来了更多可能性。RK3588作为一款高性能SoC,适合用于5G联网应用。SIM8200广和通FM160和移远RM500U。在RK3588上调试5G模块可以为设备提供稳定的高速联网能力。本文介绍了如何配置和调试SIM8200、广和通FM160和移远RM500U模块,通过AT指令和QMI/MBIM工具实现5G数据连接。

flypig has a dream! 2671

【C语言】尾递归

讲了尾递归特点及其使用思路,欢迎大家观看

2501_91427718的博客 976

用Python爬取政府采购信息的实战全流程

以 Playwright 自动采集政府采购信息结构化存储与查询可拓展关键词过滤与通知机制。

2201_76125261的博客 1146

C语言 尾递归

引用文章:尾递归 文章目录定义原理实例1、首先展示我们正常计算阶乘的代码2、尾递归 定义 如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。 原理 当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建一个新的。编译器可以做到这点,因为

Dontla的博客 1429

RK3588更新NPU驱动的方法

想要在 RK3588 上跑一下 deepseek 的大模型,发现目前的内核提供的 NPU 驱动比较低,无法支持新的大模型,所以需要更新一下我们的 NPU 驱动本次更新的NPU驱动仅适用于RK3588和RK3676系列。

荻夜的博客 2151

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

C语言之尾递归

昨天被问到了尾递归及编译器对它的处理相关,一直对它没有研究过,解释得很含糊。 回来查了下,记录如下: 递归有线性递归(普通的递归)和尾递归。 由于尾递归的特殊性,一般的编译器会做些特殊处理。因此,在效率和开销上,比普通递归好。 举个例子,计算n! 1)线性递归: type recurve(long n) {       return  (n == 1) ? 1 : n * re

Kayven@数据 1万+

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

诺禾-C语言实现尾递归求7的阶乘

如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。 当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活跃记录而不是在栈中去创建一个新的。编译器可以做到这点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也

yyone123的博客 1724

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

c语言尾递归,漫谈递归:从汇编看尾递归的优化

对于尾递归,很多人的理解仅局限于它是递归和尾调用的一个合体,比普通递归效率高。至于效率为什么高,高在哪,可能没有深究过。尾调用要说尾递归,得先说尾调用。我理解的尾调用大概是这么一种情况:函数A里面调用了函数B。函数B执行后,函数A马上返回。也就是说调用函数B(并返回执行结果)是函数A所做的最后一件事。相当于执行完函数B后,函数A也就执行完。因此在执行函数B时,函数A的栈帧其实是已经大部分没用了,可...

weixin_39588445的博客 330

c语言的尾递归,递归与尾递归(C语言)

原文:递归与尾递归(C语言)[转] 作者:archimedes 出处:http://www.cnblogs.com/archimedes/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 在计算机科学领域中,递归式通过递归函数来实现的.程序调用自身的编程技巧称为递归( recursion). 一个过程或函数在其定...

weixin_36008052的博客 621

尾递归优化 c语言,漫谈递归:从汇编看尾递归的优化

对于尾递归,很多人的理解仅局限于它是递归和尾调用的一个合体,比普通递归效率高。至于效率为什么高,高在哪,可能没有深究过。尾调用要说尾递归,得先说尾调用。我理解的尾调用大概是这么一种情况:函数A里面调用了函数B。函数B执行后,函数A马上返回。也就是说调用函数B(并返回执行结果)是函数A所做的最后一件事。相当于执行完函数B后,函数A也就执行完。因此在执行函数B时,函数A的栈帧其实是已经大部分没用了,可...

weixin_28931559的博客 428

C语言尾递归知识及代码示例

本文将介绍C语言中尾递归的概念、特点以及如何使用尾递归解决实际问题。同时,给出一个尾递归的代码示例。

2301_80220607的博客 950

各种编程语言对尾递归的支持

  版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖。如要转贴,必须注明原文网址   http://www.cnblogs.com/Colin-Cai/p/11774213.html   作者:窗户   QQ/微信:6679072   E-mail:6679072@qq.com   尾递归   这篇文章,我们讲尾递归。在递归中,如果该函数的递归形式表现在函数返...

java梦工厂 1014

【Scala】尾递归优化

以递归方式思考递归通过灵巧的函数定义,告诉计算机做什么。在函数式编程中,随处可见递归思想的运用。 下面给出几个递归函数的例子:object RecursiveExample extends App{ // 数列求和例子 def sum(xs: List[Int]): Int = if (xs.isEmpty) 1 else xs.head + sum

Jason Ding的专栏 6077

递归和尾递归(用C语言解斐波那契和阶乘问题)

什么是尾递归很多人都对递归有了解,但是为尾递归很少,所以这次来专门讲一讲关于尾递归的一些问题。什么是尾递归如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。因为在一些题目的做法中,我们可以发现递归的使用有局限性,有时候会占用相当大的空间。比如斐波那契问题,代码很容易用递归去写,但是浪费了大量的内存,一个数会重复计算多次,所以我们来使用尾递归。这里引用一个我看别人说的一句话,我认为非常对普通递归的结果是返回值,尾递归的结果是参数。完全可以这样理解。

2301_80344616的博客 1298

【C语言】尾调用和尾递归

尾递归和尾调用

2301_80424145的博客 1040
上一篇: 【C语言入门】函数调用栈
下一篇: 【C语言入门】函数返回局部变量指针的底层原理与实践陷阱
Mr_-G
Mr_-G 优质创作者: 嵌入式与硬件开发技术领域 优质创作者: 嵌入式与硬件开发技术领域
博客等级 码龄6年 7922粉丝 416原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值