81

问题的标题可能有点奇怪,但事实是,据我所知,根本没有任何东西反对尾调用优化。然而,在浏览开源项目时,我已经遇到了一些积极尝试阻止编译器进行尾调用优化的函数,例如CFRunLoopRef的实现,它充满了这样的hacks。例如:

static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    if (func) {
        func(observer, activity, info);
    }
    getpid(); // thwart tail-call optimization
}

我很想知道为什么这看起来如此重要,是否有任何情况下我作为​​一个普通的开发人员也应该记住这一点?例如。尾调用优化有常见的陷阱吗?

4

3 回答 3

83

我的猜测是,这是为了确保它__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__在堆栈跟踪中以用于调试目的。它__attribute__((no inline))支持这个想法。

如果您注意到,该函数无论如何都会跳到另一个函数,所以它是一种蹦床形式,我只能认为它有这样一个冗长的名称来帮助调试。这将特别有用,因为该函数正在调用已从其他地方注册的函数指针,因此该函数可能无法访问调试符号。

还要注意其他类似名称的函数,它们执行类似的操作 - 它看起来确实可以帮助查看回溯中发生的情况。请记住,这是核心 Mac OS X 代码,也会显示在崩溃报告和处理示例报告中。

于 2012-05-28T22:03:43.343 回答
34

这只是一个猜测,但可能是为了避免无限循环与堆栈溢出错误的轰炸。

由于所讨论的方法没有将任何东西放在堆栈上,因此尾调用递归优化似乎有可能生成进入无限循环的代码,而不是将返回地址放在堆栈上的非优化代码如果滥用,最终会溢出。

我唯一的其他想法与保留堆栈上的调用以进行调试和堆栈跟踪打印有关。

于 2012-05-28T21:56:30.407 回答
21

一个潜在的原因是使调试和分析更容易(使用 TCO,父堆栈帧消失,这使得堆栈跟踪更难理解。)

于 2012-05-28T21:59:49.013 回答