堆栈溢出导致异常终止

时间:2019-02-08 16:45:42

标签: c stack-overflow termination

最近几天前,我写了我的研究生入学考试,考试中出现了以下问题。
当使用任何正整数作为参数调用下面的函数时,它是否终止?还能打印什么吗?

void convert (int n) 
{
  if (n < 0)
    printf ("%d", n);
  else 
  {
    convert (n/2);
    printf ("%d", n%2);
  }
}

据我说,什么都不会打印,因为控件永远不会到达if语句内部,而且因为printf语句位于else块下的函数调用之后。 n的值永远不会低于0,并且函数会一次又一次地调用自身,直到堆栈溢出为止。我的问题是代码是否会由于堆栈溢出而异常终止?

3 个答案:

答案 0 :(得分:6)

由于基本条件n < 0将永远不会满足,因此代码不会以正整数参数结尾。

convert的递归调用使用参数n / 2对其进行调用,该参数作为整数除法将不可避免地到达为零,但永远不会小于零。

例如,使用参数10

call convert(10)
10 < 0 is false; enter the else branch
call convert(10 / 2)
5 < 0 is false; enter the else branch
call convert(5 / 2)
2 < 0 is false; enter the else branch
call convert(2 / 2)
1 < 0 is false; enter the else branch
call convert(1 / 2)
0 < 0 is false; enter the else branch
call convert(0 / 2)
0 < 0 is false; enter the else branch
call convert(0 / 2)
0 < 0 is false; enter the else branch
call convert(0 / 2)
0 < 0 is false; enter the else branch

它永远不会进入基本情况。

答案 1 :(得分:3)

根据编译器的输出,代码是否会因堆栈溢出而终止。

将没有输出。因为n永远不会小于0

使用不会编译递归的幼稚编译器,您将在大多数(如果不是全部)实现上获得stackoverflow。

答案 2 :(得分:3)

是的,除非您的编译器的优化程序执行了一些异常操作,否则该程序将由于堆栈溢出而异常终止。

原因是convert()函数被无数次递归调用。您已经知道了,但重点是:convert()的每个递归条目都将一个新帧推入堆栈。每帧都包含前一帧的返回地址和本地值n

编译器具有优化器,但是需要一个不寻常的优化器才能知道此函数(a)没有副作用,并且(b)没有返回值。因此,优化器不太可能保存此代码以防异常终止。

我相信您已经正确回答了这个问题。

同时,评论者使我们想起了特殊情况,即尾递归。如果递归调用已终止该函数,例如return convert(n/2);,则编译器可以为所有递归调用 reused 使用单个堆栈帧。原因:到递归调用发生时,当前调用不再需要其存储;递归调用可以安全地用新的n覆盖对象n;仅需要保留原始呼叫者的回信地址。如果应用尾递归,则堆栈将不会增长,因此,程序将不会异常终止,也不会异常终止。但是,尾递归不适用于此。