c++ - 为什么函数中的局部数组似乎可以防止 TCO?

标签 c++ tail-recursion

看起来你的函数中有一个本地数组会阻止在我检查过的所有编译器上对其进行尾调用优化:

int foo(int*);

int tco_test() {
    // int arr[5]={1, 2, 3, 4, 5}; // <-- variant 1
    // int* arr = new int[5]; // <-- variant 2
    int x = foo(arr);
    return x > 0 ? tco_test() : x;
}

variant 1 处于事件状态时,最终会真正调用 tco_test() (gcc 之前尝试做一些展开,但它仍然调用函数到底)。 变体 2 按预期执行 TCO。

本地数组中是否有某些东西导致无法优化尾调用?

最佳答案

如果编译器仍然执行 TCO,那么所有外部 foo(arr) 调用都会收到相同的指针。这是一个可见的语义变化,因此不再是纯粹的优化。

所讨论的局部变量是一个数组这一事实在这里可能是一个转移注意力的问题;重要的是它通过指针对外部的可见性。

考虑这个程序:

#include <stdio.h>

int *valptr[7], **curptr = valptr, **endptr = valptr + 7;

void reset(void)
{
  curptr = valptr;

}

int record(int *ptr)
{
  if (curptr >= endptr)
    return 1;
  *curptr++ = ptr;
  return 0;
}

int tally(void)
{
  int **pp;
  int count = 0;

  for (pp = valptr; pp < curptr; pp++)
    count += **pp;

  return count;
}

int tail_function(int x)
{
  return record(&x) ? tally() : tail_function(x + 1);
}

int main(void)
{
  printf("tail_function(0) = %d\n", tail_function(0));
  return 0;
}

随着 tail_function 递归,它通过尾调用执行,record 函数记录局部变量 x 的不同实例的地址>。当它用完空间时,它返回 1,这会触发 tail_function 调用 tally 并返回。 tally 扫描记录的内存位置并添加它们的值。

如果 tally 受 TCO 约束,那么将只有一个 x 实例。实际上,它会是这样的:

int tail_function(int x)
{
tail:
  if (record(&x))
    return tally();
  x = x + 1;
  goto tail;
}

所以现在,record 一遍又一遍地记录相同的位置,导致 tally 计算出一个错误的值,而不是预期的 21.

recordtally 的逻辑取决于 x 在作用域的每次激活时实际实例化,并且作用域的外部激活有一个持续到内在生命终止的生命。该要求阻止 tail_function 在常量空间中递归;它必须分配单独的 x 实例。

关于c++ - 为什么函数中的局部数组似乎可以防止 TCO?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38878700/

相关文章:

performance - 为什么我的Scala尾递归比while循环快?

c++ - lua_getglobal 崩溃程序

c++ - 指示文件结束

c++ - parallel_for 中定义的变量范围

javascript - 尾递归二叉树搜索函数JS

functional-programming - "Stack overflow during evaluation"与 stdlib List.map

java - java上的复活节彩蛋递归函数

recursion - 循环/递归和递归本身有什么区别?

C++ 日期算法

c++ - 未格式化的 i/o 进出内存