令人惊讶的C代码段错误

时间:2016-05-13 02:27:59

标签: c segmentation-fault factorial

我无法理解这个代码在运行时是如何导致段错误的,有人能帮我理解发生了什么吗?

#include <stdio.h>

unsigned long long factorial(unsigned long long x, unsigned long long amt)
{
    if (x == 1ULL) return amt;
    else return factorial(x-1ULL, amt*x);
}

int main(int argc, char *argv[])
{
    for (unsigned long long i = 0; i < 10ULL ;i++) {
        printf("%llu\n", factorial(i, 1ULL));
    }
}

2 个答案:

答案 0 :(得分:2)

在原始代码中:

if (x == 1ULL) return amt;

意味着此递归函数factorial的退出条件。但是,当将值0传递给函数并且假定x的类型为unsigned long long时,将首先使用参数factorial对函数x-1ULL进行递归调用x的值非常大(18446744073709551615就是我在这里得到的)。对factorial的连续递归调用将逐渐耗尽为程序分配的堆栈空间,直至出现分段错误

你应该这样做:

#include <stdio.h>

unsigned long long factorial(unsigned long long x, unsigned long long amt)
{
        if (x == 0ULL) // Changed the exit condition, see Reference [1]
                return amt; // Bear in mind that the initial value for amt you passed is 1
        amt*=x; // See Reference [2]
        return factorial(x-1ULL, amt);
}

int main(int argc, char *argv[]) {
        for (unsigned long long i = 0; i < 10ULL ;i++) {
                printf("%llu\n", factorial(i, 1ULL));
        }

}

<强>参考

  1. 使用阶乘(简单来说)的思想来计算排列一组n个数的排列(组合)的数量。可以说空集只能单向排序,所以0! = 1.检查this
  2. 我觉得这比factorial(x-1ULL, amt*x)
  3. 更具可读性

    注意

    ULL后缀在这里是多余的,可以完全删除。

答案 1 :(得分:2)

首先,段错误不一定导致指针解除引用无效。在这种情况下,它实际上是由无限递归和最终耗尽堆栈空间引起的。为什么?递归函数的基本要求是它必须在某种状态下完成和终止递归,如果仔细查看代码,在函数factorial中,如果x为0,则递归将变为无穷无尽并最终崩溃你的程序。您可以通过将终止条件更改为:

来解决此问题
if (x <= 1ULL) return amt;