x86 Intel汇编中递归的工作原理及示例代码疑问解析
Intel汇编递归求和代码详解
先看这段实现数字各位之和递归计算的Intel汇编代码:
.intel_syntax noprefix .data .text .global sum sum: enter 0, 0 mov esi, 0 # result = 0 cmp edi, 0 # if n == 0, return 0 je end mov eax, edi # preparing for division mov edx, 0 mov ecx, 10 div ecx # eax = n / 10, edx = n % 10 push rdx # save the current digit (n % 10) mov edi, eax # I don't understand this line at all, why are we moving eax back to edi? call sum # recursive call - result in eax pop rdx # get n % 10 back from stack add eax, edx # result = sum(n / 10) + (n % 10) end: leave ret
下面逐个解答你的疑问:
1. 为什么要把eax的值移回edi?
x86-64 System V调用约定里,第一个整数参数通过edi传递。当前函数的输入参数n存在edi中,执行div ecx后,eax存储的是n/10——这正是递归调用sum时需要传入的参数。把eax的值放到edi,就是为下一次递归调用准备好输入参数。
2. call sum具体执行了什么?会自动push/pop rax吗?
call指令做两件事:
- 把当前指令的下一条指令地址(也就是
pop rdx的地址)压入栈,作为递归返回后要执行的位置 - 跳转到
sum函数的入口开始执行
它不会自动push或pop rax,寄存器的内容需要你手动管理。当递归调用完成后,ret指令会把栈里保存的返回地址弹出,跳回原来的位置继续执行。
3. 为什么pop rdx后要和eax相加?
递归调用sum(n/10)的返回结果存在eax里,而之前push rdx保存的是当前数字的个位(n%10)。弹出rdx后加到eax上,正好对应递归公式:sum(n) = sum(n/10) + (n%10)
这就是计算各位数字之和的核心逻辑。
4. mov esi, 0没被使用却每次都设置,意义何在?
这行代码是冗余的——整个函数里从来没用到过esi的值,大概率是写代码时的残留(比如原本打算用esi存结果,后来改成用eax返回),完全可以删掉,不会影响函数功能。
额外补充:递归的栈工作逻辑
每次递归调用前,我们把当前的个位数字压栈保存;当递归到n=0时触发je end,开始逐层返回:
- 每次返回时弹出之前保存的个位数字,加到当前
eax的结果上 - 直到最外层调用完成,
eax里就是最终的各位数字之和
内容的提问来源于stack exchange,提问作者Danilo Jonić
相关产品推荐
相关产品推荐

