当我的阵列太大时出现分段错误

Segmentation fault when my array is too big

本文关键字:分段 错误 我的 阵列      更新时间:2023-10-16

编辑:似乎错误只是 9,999,999,999,999,999 对于数组来说太大了。

我在代码上收到此错误"程序收到信号 sigsegv 分段错误"。

基本上我的代码是做一个整数分解,练习可以在codeabbey上看到。基本上,我将接收 1000 这样的输入,并将它们输出为它们因子的乘积,在本例中为 2*2*2*5*5*5。

我通过使用埃拉托色尼筛方法生成的素数向量来执行上述操作。

根据该网站,输入的位数不会超过13,因此我的最高数字是9,999,999,999,999,999。下面是我的代码。

#include <iostream>
#include <vector>
#include <cstring>
unsigned long long int MAX_LIMIT = 9999999999999;

std::vector<unsigned long long int> intFactorisation (unsigned long long int num) {
std::vector<unsigned long long int> answers;
static std::vector<unsigned long long int> primes;
if (primes.empty()) {               // generate prime numbers using sieve method
bool *arr = new bool[MAX_LIMIT];
memset (arr, true, MAX_LIMIT);
for (unsigned long long int x = 2; x*x < MAX_LIMIT; x++) {
if (arr[x] == true) {
for (unsigned long long int y = x*x; y < MAX_LIMIT; y += x) {
arr[y] = false;  // THIS LINE ALWAYS HAS AN ERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
}
}
for (unsigned long long int x = 2; x <= MAX_LIMIT; x++) {
if (arr[x]) {
primes.push_back(x);
}
}
}
std::vector<unsigned long long int>::iterator it = primes.begin();  // start the factorisation
while(it != primes.end()) {
if (num % *it == 0) {
answers.push_back(*it);
num/=*it;
}
else {
it++;
}
if (num == 1) {
break;
}
}
return answers;
}

int main() {

int maxi;
std::cin >> maxi;
int out[maxi];
for (int x = 0; x < maxi; x++) {
std::cin >> out[x];
}
for (auto it : out) {
std::vector<unsigned long long int> temp = intFactorisation(it);
for (std::vector<unsigned long long int>::iterator it = temp.begin();
it != temp.end(); it++) {
if (it == temp.end() - 1) {
std::cout << *it << " ";
}
else {
std::cout << *it << "*";
}
}
}
}

但是,由于某种原因,程序在函数 intFactorization 中总是在 arr[y] = false 处终止。在使用 CodeBlock 时,我将在屏幕左下角看到一条通知,给出分段错误消息。

我已经在我的大得离谱的数组中使用了"new",所以内存应该在堆上。我尝试使用较小的MAX_LIMIT例如 100,000,并且我的函数可以正常工作。有人知道为什么吗?

另外,我想知道为什么我不需要取消引用我的指针 arr。例如,arr[y] = 假作品,但 *arr[y] 或 (*arr)[y] 不行。希望这也可以澄清。

感谢您阅读本文,感谢您的任何帮助。

发布的代码中有两种类型的问题,内存管理和算法。

资源获取

该程序展示了三种内存分配:

  • 可变长度数组。在main中,out被声明为int out[maxi]maxi是一个变量,而不是一个编译时常量。这是一个 C99 功能,它从未成为任何C++标准的一部分,即使它被某些编译器作为功能提供。

  • bool *arr = new bool[MAX_LIMIT];.现代指南建议避免使用裸new来分配内存,而更喜欢智能指针,标准容器,并且通常遵循RAII习惯用法,但这甚至不是这里的主要问题。MAX_LIMIT太大了,不会导致std::bad_alloc异常,而且delete永远不会被调用。

    在同一个函数中,还有一个循环,它最终会越界访问(不太可能)分配的内存:for (unsigned long long int x = 2; x <= MAX_LIMIT; x++) { if (arr[x]) {,x 最终会变得MAX_LIMIT,但你之前会耗尽内存。

  • std::vector.如果程序不尝试用MAX_LIMIT之前的所有素数填充向量primes,即 1013-1或假设为 64 位类型几乎 80TB,那就是这样。

算法

这个想法是首先计算所有可能的素数,然后对于每个输入的数字,检查其中是否有任何一个是一个因子。问题在于最大可能数是一个非常大的数字,但好消息是你不需要计算和存储所有素数,直到这个数字,而只需要计算和存储它的平方根

想象一下,尝试了所有素数直到该平方根(我们称之为 S),因此将原始数除以找到的任何因子。如果仍有余数,则该值不能被任何提到的素数整除,并且小于或等于原始数字。它本身必须是一个质数。所有可能的因素<= S都已经被排除了,所以任何假设的因素>S(除以它的结果是什么?另一个已经测试过的素数小于S)。

从设计的角度来看,我还会讨论如何在OP的代码中计算和存储素数。因式分解函数基本上写成如下。

unsigned long long int MAX_LIMIT = 9999999999999;
std::vector<unsigned long long int> intFactorisation (unsigned long long int num)
{
static std::vector<unsigned long long int> primes;
//  ^^^^^^                                           ^
if (primes.empty())
{
// generate prime numbers up to MAX_LIMIT using sieve method
}
// Use the primes...
}     

静态向量可以使用分离函数初始化并声明为const,但考虑到与因式分解函数的密切关系,最好将这些数据和函数包装到一个负责正确分配和初始化资源的类中。

自从在语言中引入 lambda 以来,我们可以避免与普通函子类相关的大部分样板文件,以便分解函数可以构造为由以下内容返回的有状态 lambda:

auto make_factorizer(uint64_t max_value)
{
uint64_t sqrt_of_max = std::ceil(std::sqrt(max_value));
// Assuming to have a function returning a vector of primes.
// The returned lambda will accept the number to be factorized and
// a reference to a vector where the factors will be stored
return [primes = all_primes_up_to(sqrt_of_max)](uint64_t number,
std::vector<uint64_t>& factors)
{
uint64_t n{number};
factors.clear();
// Test against all the known primes, in ascending order
for (auto prime : primes)
{
// You can stop earlier, if prime >= sqrt(n)
if (n / prime <= prime)
break;
// Keep "consuming" the number with the same prime,
// a factor can have a multiplicity greater than one.
while (n % prime == 0)
{
n /= prime;
factors.push_back(prime);
}
}
// If n == 1, it has already found and stored all the factors. 
// If it has run out of primes or breaked out from the loop,
// either what remains is a prime or number was <= 1. 
if (n != 1  ||  n == number)
{
factors.push_back(n);
}
};
}

在这里测试了一个实时实现。

更简单的解决方案可能是即时查找所有素因数,而不是最初列出所有素数并检查它是否是给定数字的因数。

这是在不分配大内存的情况下查找因子的函数,

std::vector<unsigned long long int> intFactorisation(unsigned long long int num) {
std::vector<unsigned long long int> answers{};
unsigned long long int Current_Prime = 2;
bool  found;
while (num!=1 && num!=0)
{
//push all the factor repeatedly until it is not divisible
// for input 12 push 2,2 and exit
while (num % Current_Prime == 0) {
answers.push_back(Current_Prime);
num /= Current_Prime;
}
//find Next Prime factor 
while (Current_Prime <= num) {
Current_Prime++;
found = true;
for (unsigned long long int x = 2; x*x < Current_Prime; x++)
{
if (Current_Prime%x == 0) {
found = false;
break;
}
}
if (found == true)
break;
}
}
return answers;
}