【问题标题】:Segmentation fault when my array is too big当我的数组太大时出现分段错误
【发布时间】:2020-06-08 07:07:37
【问题描述】:

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

我的代码出现此错误“程序收到信号 sigsegv 分段错误”。

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

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

根据网站,输入的位数不会超过13,因此我的最高数字是9,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 << "*";
            }
        }
    }

}

但是,由于某种原因,程序将始终在函数 intFactorisation 中的 arr[y] = false 处终止。使用 CodeBlocks 时,我会在屏幕左下方看到一条通知,显示分段错误消息。

我已经在我可笑的大数组上使用了“新”,所以内存应该在堆上。我曾尝试使用较小的 MAX_LIMIT,例如 100,000,并且我的功能有效。有谁知道为什么?

另外,我想知道为什么我不需要取消引用我的指针 arr。例如, arr[y] = false 有效,但 *arr[y] 或 (*arr)[y] 无效。希望这也能得到澄清。

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

【问题讨论】:

  • 考虑y == MAX_LIMIT的情况,你的代码会发生什么?
  • int out[maxi]; -- 在其他地方使用用过的std::vector。你为什么不在这里也用它?
  • 除非sizeof(bool) 是一,否则memset (arr, true, MAX_LIMIT); 不会按照您的想法执行,但会导致未定义的行为。
  • 如果sizeof(bool) 1,则您正尝试为该数组分配 10 TB。如果是 4 个(很可能),那就是 40 TB。你真的有那么多闲钱吗?
  • @user6088862 "如果这是问题,为什么使用较小的 MAX_LIMIT 有效?" 未定义的行为是未定义的。一种可能的表现形式是:看起来有效。

标签: c++ arrays segmentation-fault


【解决方案1】:

贴出的代码有两种类型的问题,内存管理和算法。

资源获取

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

  • 可变长度数组。在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 &lt;= MAX_LIMIT; x++) { if (arr[x]) {,x 最终将变为 MAX_LIMIT,但您之前会耗尽内存。

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

算法

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

想象一下,尝试了直到平方根(我们称之为 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);
        }
    };
}

Here 一个实时实现已经过测试。

【讨论】:

    【解决方案2】:

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

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

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 2014-12-14
      • 1970-01-01
      • 2020-06-14
      • 1970-01-01
      • 2019-05-11
      • 1970-01-01
      • 2020-08-02
      • 1970-01-01
      • 2012-01-01
      相关资源
      最近更新 更多