我正在尝试尽可能多地用 C 编写函数式程序。我知道像 GCC/Clang 这样的编译器会默默地进行尾调用优化,但不能保证。是否有任何选项可以强制对编译器进行尾调用优化?(当然,只有在它自己的末尾才被调用)
7 回答
Clang 13 “musttail”属性强制尾递归函数中的尾调用优化,即使优化被禁用。
https://clang.llvm.org/docs/AttributeReference.html#musttail
用法:
int f(int x) {
...
__attribute__((musttail)) return f(x-1);
}
Clang 根本没有做任何优化。有一个 LLVM 通行证tailcallelim
可以做你想做的事(但不能保证)。您可以使用opt
.
我不认为它真的强制执行它,但你可以-foptimize-sibling-calls
在使用gcc
. 如果您使用-O2
,-O3
或,它会自动启用-Os
。
一般来说,我建议不要在 C 中过度使用递归。如果你真的想做 fp,请选择一种函数式语言。在某些情况下它是合适的,例如快速排序,buf,如果您习惯于使用递归而不是循环,那么您很有可能会破坏堆栈。
元答案:
从函数式语言过渡到 C 语言有一些有用的教训:使用小函数,使用不会改变全局变量或输入参数的函数,不要害怕函数指针。但是你在这里可以合理地做的事情是有限的,依赖尾调用消除('尾调用优化'并不是真正正确的术语)可能超出了有用的范围。你不能强迫编译器使用这个策略,即使你可以,生成的 C 语言也会非常单调,并且很难被其他人阅读,包括你未来的自己。
发挥语言的优势。C对某些事情有好处,因此以良好的 C 风格将其用于那些事情。如果您想要不同的优势,或者如果您想使用函数式风格(绝妙的决定!),请使用函数式语言。
Alpha 和 i386 的 GCC 扩展描述如下:
Mark Probst,2001 年的C 语言中的适当尾递归(文凭论文) 。
实际上,很多 C 编译器已经为您处理了这个问题。正如 eq 所提到的,您最好让编译器处理大部分这些事情,而不是尝试创建在其他地方不起作用的优化。很多时候,即使您设置了优化标志,您也会发现实际上并没有性能差异。
如果它确实是尾调用,那么 while 循环或 goto 看起来与递归调用没有太大区别。只需更新所有变量,而不是将它们作为参数传递。AFAIK 这是 C 语言中在所有优化级别控制堆栈使用的唯一跨平台方式。它实际上也更具可读性,因为您有一个初始化函数,然后是循环,这是非常惯用的。尾递归版本需要两个函数,一个用于初始化,一个用于递归部分。