c++ - 分段错误(核心转储)在 Eratosthenes 的筛子上尝试非常大的数字
问题描述
我必须找出小于给定 N 的数字有多少是素数,N <= 5 * 10 ^ 7
我试图声明数字long long
, int long
,并且我收到非常高的值的分段错误错误。
代码是:
#include <bits/stdc++.h>
using namespace std;
long long int counter;
void SieveOfEratosthenes(long long int n)
{
bool prime[n + 1];
memset(prime, true, sizeof(prime));
for (long long int p = 2; p * p <= n; p++)
{
if (prime[p] == true)
for (long long int i = p * p; i <= n; i += p)
prime[i] = false;
}
for (long long int p = 2; p <= n; p++)
if (prime[p])
counter++;
cout << counter;
}
int main()
{
long long int n;
cin >> n;
SieveOfEratosthenes(n);
return 0;
}
解决方案
正如我所怀疑的,根据我的 gdb,段错误发生在memset
(测试 N = 10000000):
Program received signal SIGSEGV, Segmentation fault.
0x0000555555554999 in SieveOfEratosthenes (n=10000000) at soe.cpp:8
8 memset(prime, true, sizeof(prime));
这是在数组初始化之后立即执行的,因此实际上是“开箱即用”。问题是您在堆栈上声明了一个巨大的数组,并且那里没有足够的内存。你的内存用完了。
要访问计算机的其余内存,您需要在堆上创建一个数组。在大多数情况下,最好的方法是使用标准 C++ 库类std::vector
。声明后,它就像一个数组一样有效地工作,但它会为你做内存管理。您也可以使用malloc
or更“直接”地做到这一点new
,但是您必须自己管理内存(特别是,您需要free
ordelete
完成后,在函数返回之前,因为如果你不这样做,你会得到一个“内存泄漏”:至少只要程序正在运行,就会有一块内存在附近,可能非常大,直到您的操作系统在程序停止后回收它之前,什么都做不了,因为您丢失了指向它的指针。虽然在这里可能不是问题,但您不希望在更严重的应用程序中发生这种情况。)。
当然,一旦达到计算机内存的绝对限制,程序最终还是会失败,因此您应该包括进一步的处理代码(特别是检查 malloc 或 new 返回的指针是否为 NULL,或者是否使用 std::向量,使用 try/catch 中std::bad_alloc
代码周围的 [IIRC] 异常SieveOfEratosthenes
来优雅地处理这种情况(例如,显示“没有足够的内存来执行这么大的运行”消息)。
ADD:将所有这些更改放在一起,它应该看起来像
//#include <bits/stdc++.h> don't use this include. Can be very harmful
// if you aren't careful. And can be very harmful even then.
#include <iostream> // list all of the headers you need
#include <vector>
#include <algorithm> // for std::fill, see below
using namespace std; // fyi. generally not good practice to use
// "using namespace std" lest it become a habit
// (e.g. what if you make another function called "fill"
// below?)
long long int counter; // fyi 2. does not need to be global
void SieveOfEratosthenes(long long int n)
{
try {
vector<bool> prime(n + 1);
fill(prime.begin(), prime.end(), true); // has same fx as memset.
// could also use
// vector<bool> prime(n + 1, true);
// to allocate and fill at the same time.
for (long long int p = 2; p * p <= n; p++)
{
if (prime[p] == true)
for (long long int i = p * p; i <= n; i += p)
prime[i] = false;
}
for (long long int p = 2; p <= n; p++)
if (prime[p])
counter++;
cout << counter << endl; // "endl" to give a newline to make it nice
} catch(const std::bad_alloc &) { // prefer to catch by constant
// reference
cout << "Sorry, not enough memory to do a run that large." << endl;
}
}
int main()
{
long long int n;
cin >> n;
SieveOfEratosthenes(n);
return 0;
}
推荐阅读
- javascript - 如何在我的函数中嵌套 setInterval 和 setTimeout?
- postgresql - 如何在 PostgreSQL for Azure 上使用或安装 pgAgent
- sql - 从小时表更新汇总表
- c++ - 使用从 .csv 文件(stod、strtod、atof)中提取的数据在 C++ 中从字符串转换为双精度
- javascript - 如何使用 JavaScript 在 HTML 中选择语言输出?
- python - 将 numpy 数组显示为带有动画的图像
- docker - 是否可以在使用 Docker-compose 一起构建和运行 docker 时传递 --build-arg LEnv=dev UEnv=Dev
- c++ - 如何连接格式字符串?
- button - 我可以增加点击按钮的点击持续时间吗
- python - 如何使用 webbrowser 从 Python 脚本启动 Microsoft Edge