2

所以我在 C 中玩递归,我不知道为什么会这样:
代码 A

int foo(int x)
{
 if (x==0) return 0;
 else return foo(--x)+x;
}

int main() { printf("%d\n", foo(10));

代码 B

int foo(int x)
{
 if (x==0) return 0;
 else return foo(x--)+x;
}

int main() { printf("%d\n", foo(10));

代码 A 打印 45 而不是 55,我想通了。这是因为递归调用以这种方式展开:9+8+7+...+0 = 45

另一方面,代码 B 被卡住并且永远不会返回提示!我必须这样ctrl+c做。为什么会卡住?是因为它从不超过 10 吗?

4

5 回答 5

9

这是因为在代码片段 B x 在递归调用之后递减,所以函数总是用 foo(10) 调用

编辑:
正如 James McNellis 在其评论中指出的那样, x 在调用函数之前会递减,但它的原始值在调用中使用而不是递减的值。
基本上,表达式 x-- 在 foo(x--) 之前计算,但它的值实际上是 x,因此函数以 x = 10 调用

于 2012-08-21T21:30:26.870 回答
2

后减量(x--)等待函数调用在执行之前返回。所以你的堆栈看起来像:

foo(10)
foo(10)
foo(10)
...
于 2012-08-21T21:33:26.837 回答
1

表达式的值x--是 的当前值x,在存储值递减之前。因此,在调用foo(x--)时,参数的值foo始终与其当前值相同,从而产生无限递归。


但是请注意,这两个程序的行为可能是未定义的,并且取决于 和 的子表达式的求值foo(x--) + x顺序foo(--x) + x。让我们考虑第一个程序,它包含表达式

foo(--x) + x

如果按以下(有效!)顺序评估子表达式,则程序具有未定义的行为:

  1. x被读取以计算 的值x作为 的右侧操作数+

  2. x被读取以计算 的值x作为 的操作数--

  3. x被写入以存储 的新值x递减后

  4. foo叫做

这里唯一的顺序点在第 3 步和第 4 步之间:在计算参数表达式之后,在调用函数之前。因为x读取两次(在 [1] 和 [2] 中)并写入一次(在 [3] 中)并且因为其中一次读取(在 [1] 中)不是为了计算要存储的新值,所以行为未定义。

评估顺序未指定,但如果此评估顺序是使用的顺序,则程序具有未定义的行为。

同样的推理同样适用于第二个程序。

最好x - 1用作 的参数foo,因为此表达式不会修改 的值x

于 2012-08-21T21:40:54.773 回答
0

是的,它永远不会减少。

    else return foo(x--)+x;

在调用函数的行中,使用后递减,这意味着值 10 首先传递给下一个函数,然后递减 - 这永远不会发生,因为没有函数返回

于 2012-08-21T21:32:45.077 回答
0

打电话

foo(--x)

使用已经递减的值调用函数,而

foo(x--)

总是用原始值(即10)调用它,所以它永远不会接近零,这就是原因。

于 2012-08-21T21:33:03.763 回答