为什么从工厂函数分配向量并在循环中迭代它会导致段错误

Why does assigning a vector from a factory function and iterating over it inside a loop cause a segfault?

本文关键字:段错误 错误 迭代 函数 工厂 分配 向量 循环 为什么      更新时间:2023-10-16

我写了一个函数,将一个数字分解为一个较小的数字序列,使得平方序列元素的总和等于输入数字的平方。此函数的声明如下所示:std::vector<long long> Decomp::decompose(long long n); 。为了测试函数的输出,我使用以下main函数:

int main()
{
    for (long long n = 1; n <= 65; ++n)
    {
        auto res = Decomp::decompose(n);
        if (res.empty())
            std::cout << n << ": No solution.n";
        else
        {
            std::cout << n << ": [";
            for (long long i: res)
                std::cout << i << ' ';
            std::cout << "b]n";
        }
    }
}

但是,这会导致n=8的分段错误:

Output:
1: No solution.
2: No solution.
3: No solution.
4: No solution.
5: [3 4]
6: No solution.
7: [2 3 6]
8: [5 4 3 2 1 0 72405692564177152 72340172838076673 281474993553665 130961 139767842405240 139767842405240 4366240942559068160 72340172838076673 72340172838076673 282578800148737 1099511628033 256 72340168526266368 72340172838091928 72340172838076673 72340172838076673 72340172838076673 72340172838076673 72057594054770689 72340173837828353 72340172838076673 72340172838076673 72340172838076673 72340172838076673 72340172838076673 72405692564177153 72340172838076673 72340172838076673 72340172838076673 72340172838076673 72340172838076673 16843009 240 32 17817984 4 272 32 0 3 304 48 0 4 3 2 72339069014704384 130609 257 130593 0 0 0 0 0 0 282574488404224 72340168543109376 0 0 0 0 0 0 72339069031481344 72339073326448640 72058697861300225 257 0 0 0 0 72057598349672448 72058697861300225 72340172838011137 72058697861366017 282578783305985 65793 0 0 1103806595072 282578783305985 72340172821299457 282578800148737 72340168526332161 72340168543109376 72340172838076673 72339069031481600 282578800148481 72340168526332160 72340168543109376 72340172838076673 72339069031481600 72340172838076673 72340172838076673 72340172838076673 72340172838076673 72339069031481344 72339073326448641 72340172838076673 72057598349737985 72340172838076673 72340172838076673 72340172838076673 282578800148737 130145 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 (whole lot of zeros...)  Segmentation fault

我怀疑上面的函数main某些内容导致了段错误,因为以下main函数似乎工作正常:

int main()
{
    long long n = 8;
    auto res = Decomp::decompose(n);
    if (res.empty())
        std::cout << n << ": No solution.n";
    else
    {
        std::cout << n << ": [";
        for (long long i: res)
            std::cout << i << ' ';
        std::cout << "b]n";
    }
}
Output:
8: No solution.

编辑:GDB说:

Core was generated by `./test'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00000000004026e3 in main ()

Decomp::decompose看起来像这样:

std::vector<long long> Decomp::decompose(long long n)
{
    if (n <= 1)
        return std::vector<long long>();
    size_t rows = n - 1, cols = n * n + 1;
    bool *table = new bool[rows * cols];
    std::vector<long long> ret;
    for (size_t i = 0; i < rows; ++i)
        table[i * cols] = true;
    table[1] = true;
    for (size_t i = 1; i < rows; ++i)
        for (size_t j = 1; j < cols; ++j)
        {
            table[i * cols + j] = (j < (i + 1) * (i + 1)) ? 
                                  table[(i - 1) * cols + j] :
                                  table[(i - 1) * cols + j] ||
                                  table[(i - 1) * cols + j - (i + 1) * (i + 1)];
        }
    if ( table[rows * cols - 1] )
    {
        backtrace(n, rows - 1, cols - 1, table, ret);
        std::reverse(ret.begin(), ret.end());
    }
    delete[] table;
    return ret;
}

Decomp::backtrace是一个递归函数,它从 decompose 中创建的表中提取序列,只是将其添加到此处以供参考:

bool Decomp::backtrace(long long n, size_t i, size_t j, bool table[], std::vector<long long> &seq)
{
    if (std::inner_product(seq.begin(), seq.end(), seq.begin(), 0LL) == n * n)
        return true;
    size_t cols = n * n + 1;
    if (table[i * cols + j])
    {
        if ((i + 1) * (i + 1) <= j)
        {
            seq.push_back(i + 1);
            if (backtrace(n, i - 1, j - (i + 1) * (i + 1), table, seq))
                return true;
            return backtrace(n, i - 1, j, table, seq);
        }
        else
        {
            while (table[i * cols + j] and (i + 1) * (i + 1) > j)
                --i;
            if (table[i * cols + j])
            {
                seq.push_back(i + 1);
                if (backtrace(n, i - 1, j - (i + 1) * (i + 1), table, seq))
                    return true;
                return backtrace(n, i - 1, j, table, seq);
            }
            else
            {
                std::cout << "case foundn";
                seq.pop_back();
                return false;
            }
        }
    }
    else
    {
        seq.pop_back();
        return false;
    }
}

以及供参考的类标头:

public:
    static std::vector<long long> decompose(long long n);
private:
    static bool backtrace(long long n, size_t i, size_t j, bool table[], std::vector<long long> &seq);

您的问题在于它返回带有垃圾值的 res 对象auto res = Decomp::decompose(n);。考虑此输出8: [5 4 3 2 1 0 72405692564177152 72340172838076673..... 0 之后的所有输出似乎都是垃圾值。因此,请提出一些条件并for (long long i: res)或纠正Decomp::decompose(n)中断循环。

调试会话后,我发现了问题:bool *table变量被初始化为以下行中的垃圾值:bool *table = new bool[rows * cols]; 。我希望这会将布尔值初始化为 false。非零垃圾值欺骗程序认为有n=8的解决方案,因此调用backtrace。但是,backtrace只有在有解决方案时才有效。否则,索引操作将超出范围。

我通过在表分配中调用 bool 的默认构造函数来修复它,该构造函数将布尔值设置为 false:bool *table = new bool[rows * cols]();

感谢 cantordust 建议尝试断点。