内存错误低于在C++年实现埃拉托色尼筛分时的预期

Memory error at lower limit than expected when implementing Sieve of Eratosthenes in C++

本文关键字:分时 埃拉托 实现 错误 C++ 内存      更新时间:2023-10-16

我的问题与这里描述的问题有关。我写了一个埃拉托色尼筛的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解释器

,默认设置。

将注释转换为完整答案:

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

关于堆栈/堆的一般参考:链接、链接

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

  • 使用vector<bool> is_idx_prime(target);,它在内部执行堆分配,并在向量超出范围时为您解除分配内存。这是最方便的方法。

  • 使用智能指针管理分配:auto is_idx_prime = std::make_unique<bool[]>(target);当数组超出范围时,这也将自动释放内存。

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