如果bar调用bar(i / 2),如果i是偶数整数,则为bar(3 * i + 1),否则递归函数栏将进行尾递归。
const int bar(const int i)
{
if (i < 2) return i;
return i % 2 ? bar(i/2) : bar(3*i + 1);
}
但是,如果bar调用bar或foo,那么从bar调用一组完全不同的局部变量会怎么样?
const int bar(const int i)
{
if (i < 2) return i;
return i % 2 ? bar(i/2) : foo(3*i + 1);
// where foo is very complicated recursive call that has
// 11 different user-defined/primitive type of
// local variables that can't be optimized out
}
我的理解是尾递归优化将使用调用者的堆栈。在调用被调用者之前,调用者已经完成了本地变量。因此,被调用者可以重用它。当调用者和被调用者具有相同的功能时,我的理解听起来很好(例如,foo调用foo,bar调用bar)。但是,如果堆栈大小和布局完全不同,并且callees可能是具有不同堆栈布局的多个不同函数之一,那么会发生什么?
首先,它是尾递归吗? (现在,我明白可以应用尾部“调用”优化,但这不是尾部“递归”。)其次,主要的编译器,如gcc,clang等,会优化这种尾调用? (答案似乎是肯定的。)
如果被调用者(第二个代码示例中的foo)要复杂得多怎么办?如果被调用者和调用者正在相互调用(相互递归),那么我的第二个代码示例中的调用是否会进行尾调用优化?如果被调用者(例如foo)是一个复杂的递归调用,绝对不是尾递归,并且编译器很难将它减少到循环左右,它是否仍然是尾调用优化的?
答案 0 :(得分:4)
术语&#34;尾递归&#34;是呼叫站点的本地属性。它在同一方法中的其他调用完全没有受到影响。
粗略地说,如果在返回和返回封闭方法之间不需要运行可执行代码,则调用是尾递归的。
因此,您示例中对bar()
的所有调用都是尾递归的。
但请注意,如果你说
return i % 2 ? bar(i/2) : 1 + bar(3*i + 1);
然后第一个调用是尾递归的,但第二个调用不是因为添加1 +
必须在返回后执行。
答案 1 :(得分:2)
尾递归优化是否适用于此函数?
是的,尾递归优化适用于您的示例。请查看第二个样本的汇编程序https://godbolt.org/g/cSpUZw。应用的渐进式优化。递归被循环替换。
bar(int):
cmp edi, 1
jg .L12
jmp .L6
.L15:
sar edi
cmp edi, 1
je .L14
.L12:
test dil, 1
jne .L15
lea edi, [rdi+1+rdi*2]
jmp foo(int)
.L14:
mov eax, 1
ret
.L6:
mov eax, edi
ret