栈的应用补充:
迷宫问题
二进制问题
递归问题
在传统的递归中,典型的模型是首先执行递归调用,然后获取递归调用的返回值并计算结果。以这种方式,在每次递归调用返回之前,您不会得到计算结果。传统地递归过程就是函数调用,涉及返回地址、函数参数、寄存器值等压栈(在x86-64上通常用寄存器保存函数参数),CTF-PWN的很多基础性漏洞EXP核心其实也是函数栈的问题。
传统递归的缺点:
- 效率低,占内存
- 如果递归链过长(可以理解为对应的树的深度大),可能会statck overflow(栈溢出)
尾递归,形式上函数尾部返回自身
// 连乘操作的尾递归,不需要消栈
int factorial(int n)
{
return n == 0 ? 1 : n * factorial(n - 1);// 无需返回factorial(n)
}
//GCC开O1优化生成的汇编代码是通过函数调用,非尾递归的;
factorial:
.LFB22:
.cfi_startproc
pushq %rbx // push the old n, means n + 1
.cfi_def_cfa_offset 16
.cfi_offset 3, -16 // maybe update the frame pointer?
movl %edi, %ebx
movl $1, %eax
testl %edi, %edi // test if n == 0
je .L2 // if is true, jumpt to .L2
leal -1(%rbx), %edi // %edi = n - 1
call factorial
imull %ebx, %eax // result = n * factorial(n - 1)
.L2:
popq %rbx // pop the old n
.cfi_def_cfa_offset 8
ret
.cfi_endproc
//O2优先级,则GCC会作尾递归优化:
factorial:
.LFB22:
.cfi_startproc
testl %edi, %edi
movl $1, %eax
je .L2
.p2align 4,,10
.p2align 3
.L3:
imull %edi, %eax
subl $1, %edi
jne .L3 // It's a loop
.L2:
rep
ret
.cfi_endproc
参考
后缀表达式 & 符号匹配问题