【问题标题】:C++ Array Allocation Segmentation Fault 11 NewbieC++数组分配分段错误11新手
【发布时间】:2013-02-17 02:13:20
【问题描述】:

我正在通过 Robert Sedgewick 的 C++ 算法学习 C++。现在我正在研究 Eratosthenes 筛,用户指定了最大素数的上限。当我以最大 46349 运行代码时,它会运行并打印出最多 46349 的所有素数,但是当我以最大 46350 运行代码时,会发生分段错误。有人可以帮忙解释一下原因吗?

./sieve.exe 46349
 2 3 5 7 11 13 17 19 23 29 31 ...

./sieve.exe 46350
 Segmentation fault: 11

代码:

#include<iostream>

using namespace std;

static const int N = 1000;

int main(int argc, char *argv[]) {
    int i, M;

    //parse argument as integer
    if( argv[1] ) {
        M = atoi(argv[1]);
    }

    if( not M ) {
        M = N;
    }

    //allocate memory to the array
    int *a = new int[M];

    //are we out of memory?
    if( a == 0 ) {
        cout << "Out of memory" << endl;
        return 0;
    }

    // set every number to be prime
    for( i = 2; i < M; i++) {
        a[i] = 1;
    }

    for( i = 2; i < M; i++ ) {
        //if i is prime
        if( a[i] ) {
            //mark its multiples as non-prime
            for( int j = i; j * i < M; j++ ) {
                a[i * j] = 0;
            }
        }
    }

    for( i = 2; i < M; i++ ) {
        if( a[i] ) {
            cout << " " << i;
        }    
    }
    cout << endl;

    return 0;
}

【问题讨论】:

  • 分段错误通常是由超出分配的内存范围的写入引起的。您应该使用调试器(或添加打印语句)来跟踪程序的进度,以便找出发生这种情况的时间点。
  • 请注意,如果分配失败,new 将不会返回 NULL(除非指定了 nothrow,它不在此处)。它会抛出一个std::bad_alloc 异常。

标签: c++ memory-management segmentation-fault sieve-of-eratosthenes


【解决方案1】:

你这里有整数溢出:

        for( int j = i; j * i < M; j++ ) {
            a[i * j] = 0;
        }

46349 * 46349 不适合 int

在我的机器上,将j 的类型更改为long 可以为更大的输入运行程序:

    for( long j = i; j * i < M; j++ ) {

根据您的编译器和架构,您可能必须使用long long 才能获得相同的效果。

【讨论】:

    【解决方案2】:

    当你用调试器运行你的程序时,你会看到它失败了

    a[i * j] = 0;
    

    i * j 溢出并变为负数。这个负数小于M,这就是为什么它再次进入循环然后访问a[-2146737495]失败。

    【讨论】:

    • 学究气,我敢肯定知道这一点,但从技术上讲,它不一定是-2146737495。
    • @chris 这只是从 gdb 剪切和粘贴的。
    【解决方案3】:

    我明白了,问题在于将 M 声明为 int。当我声明 i、M 和 j 时,这似乎工作正常。

    【讨论】:

    • 我不会指望long 这样做。 long 至少为 32 位,任何为 32 位的实现都会导致溢出。 long long 至少是 64,它是 C++11 的正式一部分。如果处理器是 32 位并且大量处理 64 位数字,那么速度可能会出现问题,但在我有时间之前,我什至不知道会产生多大的差异。
    • 正如NPE 所说,如果int 是32 位,那么i * j 将溢出并且可能发生坏事(如崩溃)。如果long 有更多位,则将其设为long 将起作用,对于您和您的编译器而言,long 可能是 64 位,因此i * j 不再溢出。
    【解决方案4】:

    在任何相当现代的 C++ 中,如果分配失败,您将不会从 new 返回空指针,除非您使用非抛出 new。您的那部分代码不会像您期望的那样工作 - 您必须捕获可能从调用 new 时发出的 std::bad_alloc

    您还希望将数组索引声明为类型size_t

    【讨论】:

    • 你能推荐任何关于这方面的学习资料吗?我对这种语言完全陌生,还没有接触过内存管理。
    • @DavidWilliams,good book 总是一个好的开始。请记住,现代 C++ 不像 RAII 那样涉及内存管理,您不必在其中管理它。
    猜你喜欢
    • 2014-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多