我正在尝试尽可能用C编写功能样式的程序。 我知道像GCC / Clang这样的优秀编译器可以默默地进行尾部调用优化,但是不能保证。是否有任何选项可以强制编译器进行尾调用优化? (当然只有在它自己结束时才被调用)
答案 0 :(得分:6)
Clang根本没有做任何优化。有一个LLVM传递tailcallelim
可以做你想要的(但不保证)。您可以使用opt
单独运行。
答案 1 :(得分:3)
Clang 13“musttail”属性强制尾递归函数中的尾调用优化,即使优化被禁用。
https://clang.llvm.org/docs/AttributeReference.html#musttail
用法:
int f(int x) {
...
__attribute__((musttail)) return f(x-1);
}
答案 2 :(得分:1)
一个元回答:
从函数式语言中接收C语言有一些教训是有用的:使用小函数,使用不会改变全局或输入参数的函数,不要害怕函数指针。但是,你可以合理地做到这一点是有限制的,并且依赖于尾部呼叫消除('尾部呼叫优化'并不是真正的权利术语)可能超出了有用的范围。你不能强迫编译器使用这个策略,即使你可以,生成的C也会非常单一,而且很难为其他人阅读,包括你未来的自我。
使用语言来发挥自己的优势。对于某些事情,C 是好的,所以要用它们做好C风格。如果您想要不同的优势,或者想要使用功能样式(出色的决定!),请使用函数式语言。
答案 3 :(得分:1)
我认为并不能真正实现,但是使用-foptimize-sibling-calls
时可以使用gcc
。如果您使用-O2
,-O3
或-Os
,则会自动启用该功能。
答案 4 :(得分:1)
Alpha 和 i386 的 GCC 扩展在:
Proper Tail Recursion in C (Diploma Thesis) by Mark Probst,2001 年。
答案 5 :(得分:0)
实际上很多C编译器已经为你处理了这个问题。正如eq所提到的,你可以让编译器处理大部分这些事情,而不是试图创建在其他地方不起作用的优化。通常情况下,即使您设置了优化标记,也确实没有性能差异。
答案 6 :(得分:0)
如果它确实是一个尾调用,那么while循环或goto看起来与递归调用看起来不同。只需更新所有变量,而不是将它们作为参数传递。 AFAIK这是C中唯一能够在所有优化级别控制堆栈使用的跨平台方式。它实际上可以更具可读性,因为你有一个函数初始化后跟循环,这是非常惯用的。尾递归版需要两个函数,一个用于初始化,另一个用于递归部分。