【问题标题】:Memory error at lower limit than expected when implementing Sieve of Eratosthenes in C++在 C++ 中实现 Eratosthenes 筛时,内存错误低于预期
【发布时间】:2020-10-23 20:35:57
【问题描述】:

我的问题与here 描述的问题有关。如果我将目标值设置得太高,我已经编写了一个 Eratosthenes 筛子的 C++ 实现,它会导致内存溢出。正如该问题中所建议的,我可以通过使用布尔值 <vector> 而不是普通数组来解决问题。

但是,我在n = 1 200 000 附近遇到了比预期低得多的内存溢出值。上面链接的线程中的讨论表明,普通的 C++ 布尔数组为每个条目使用一个字节,因此使用 2 GB 的 RAM,我希望能够到达 n = 2 000 000 000 的某个位置。 为什么实际内存限制如此要小得多?

为什么使用<vector>(将布尔值编码为位而不是字节)会使可计算限制超过八倍?

这是我的代码的一个工作示例,n 设置为一个小值。

#include <iostream>
#include <cmath>
#include <vector>

using namespace std;

int main() {
    // Count and sum of primes below target

    const int target = 100000;

    // Code I want to use:
    bool is_idx_prime[target];

    for (unsigned int i = 0; i < target; i++) {
        // initialize by assuming prime
        is_idx_prime[i] = true;
    }
    // But doesn't work for target larger than ~1200000

    // Have to use this instead
    // vector <bool> is_idx_prime(target, true);

    for (unsigned int i = 2; i < sqrt(target); i++) {
        // All multiples of i * i are nonprime
        // If i itself is nonprime, no need to check
        if (is_idx_prime[i]) {
            for (int j = i; i * j < target; j++) {
                is_idx_prime[i * j] = 0;
            }
        }
    }

    // 0 and 1 are nonprime by definition
    is_idx_prime[0] = 0; is_idx_prime[1] = 0;

    unsigned long long int total = 0;
    unsigned int count = 0;
    for (int i = 0; i < target; i++) {
        // cout << "\n" << i << ": " << is_idx_prime[i];
        if (is_idx_prime[i]) {
            total += i;
            count++;
        }
    }
    cout << "\nCount: " << count;
    cout << "\nTotal: " << total;
    return 0;
}

输出

Count: 9592
Total: 454396537
C:\Users\[...].exe (process 1004) exited with code 0.
Press any key to close this window . . .

或者,更改 n = 1 200 000 会产生

C:\Users\[...].exe (process 3144) exited with code -1073741571.
Press any key to close this window . . .

我在 Windows 上使用默认设置的 Microsoft Visual Studio 解释器。

【问题讨论】:

  • 你的数组是在栈上分配的,而向量是在堆上分配的。堆栈的大小比堆更受限制。要在堆上分配一个数组,你可以使用bool *is_idx_prime = new bool[target];,然后是delete[] is_idx_prime;。或者,auto is_idx_prime = std::make_unique&lt;bool[]&gt;(target);,如果您想要自动解除分配(这是个好主意)。
  • 如果要增加堆栈大小:stackoverflow.com/q/40157847/3684343
  • @f9c69e9781fa194211448473495534 请不要先建议new[]delete[]。这是最后的手段。
  • 我正在使用 Microsoft Visual Studio 解释器 -- 不,它是编译器,而不是解释器。然后是:for (unsigned int i = 2; i &lt; sqrt(target); i++) {——每次循环迭代时,您都在计算sqrt。只需计算一次,然后存储该值 - 然后使用该值。

标签: c++ visual-studio out-of-memory


【解决方案1】:

将评论变成完整的答案:

您的操作系统在内存中保留了一个特殊部分来表示程序的调用堆栈。每个函数调用都会将一个新的堆栈帧推入堆栈。如果函数返回,则从堆栈中删除堆栈帧。堆栈帧包括函数参数的内存和函数的局部变量。剩余的内存称为堆。在堆上,可以进行任意内存分配,而堆栈的结构由程序的控制流控制。为堆栈保留了有限的内存,当它变满时(例如,由于嵌套函数调用过多或本地对象太大),您会遇到堆栈溢出。出于这个原因,应该在堆上分配大对象。

关于堆栈/堆的一般参考:LinkLink

要在 C++ 中在堆上分配内存,您可以:

  • 使用vector&lt;bool&gt; is_idx_prime(target);,它在内部进行堆分配并在向量超出范围时为您释放内存。这是最方便的方式。

  • 使用智能指针来管理分配:auto is_idx_prime = std::make_unique&lt;bool[]&gt;(target); 这也会在数组超出范围时自动释放内存。

  • 手动分配内存。我只是出于教育目的而提到这一点。正如 Paul 在 cmets 中提到的,通常不建议手动分配内存,因为您必须再次手动释放内存。如果你有一个有很多内存分配的大型程序,你不可避免地会忘记释放一些分配,从而造成内存泄漏。当您有一个长时间运行的程序时,例如系统服务,创建重复的内存泄漏最终会填满整个内存(从个人经验来看,这在实践中绝对会发生)。但理论上,如果您想手动分配内存,您可以使用bool *is_idx_prime = new bool[target];,然后再使用delete [] is_idx_prime 重新分配。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-19
    • 1970-01-01
    • 2017-01-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多