繁体   English   中英

传递大堆栈分配数组作为函数参数导致堆栈溢出

[英]Passing Large Stack Allocated Array as Function Argument Causes Stack Overflow

使用 setrlimit 调整堆栈大小后,我在堆栈上分配了一个大数组。 当这个数组在 main() 中声明,然后作为参数传递给一个方法时,我得到一个分段错误。 当数组在方法中声明为局部变量时,代码运行时不会出现任何段错误。 我在具有 8GB RAM 的 Amdx86-64 linux 机器上运行代码。

#include <iostream>
#include <sys/resource.h>

using usll = unsigned long long;
void sumArray0();
double sumArray1(double c[], usll dim); 

int main(int argc, char* argv[])
{
    const rlim_t stackSize = 3 * 1024UL * 1024UL * 1024UL;
    struct rlimit rl;
    int result;

    printf("The required value of stackSize is %lu\n", stackSize);  
    result = getrlimit(RLIMIT_STACK, &rl);

    if (result == 0)
    {
        if (rl.rlim_cur < stackSize)
        {
            rl.rlim_cur = stackSize;
            result = setrlimit(RLIMIT_STACK, &rl);

            if (result != 0)
            {
                fprintf(stderr, "setrlimit returned result = %d\n", result);
            }
            else
            {
                printf("The new value of stackSize is %lu\n", rl.rlim_cur);
            }
        }
    }

    // // This seg faults
    // const usll DIM = 20000UL * 18750UL;  
    // double c[DIM];

    // for (usll i{}; i<DIM; ++i)
    // {
    //     c[i] = 5.0e-6;
    // }
    // double total = sumArray1(c, DIM); // Seg fault occurs here

    sumArray0(); // This works

    std::cout << "Press enter to continue";
    std::cin.get();

    return 0;
}

void sumArray0()
{
    double total{};
    const usll DIM = 20000UL * 18750UL; 
    double c[DIM];

    for (usll i{}; i<DIM; ++i)
    {
        c[i] = 5.0e-6;
    }

    for (usll i{}; i<DIM; ++i)
    {
        total += c[i];
    }

    std::cout << "Sum of the elements of the vector is " << total << std::endl;
}

double sumArray1(double c[], usll dim)
{
    double total{};

    for (usll i{}; i<dim; ++i)
    {
        total += c[i];
    }

    return total;
}

我的问题是:
为什么我在第一种情况下会遇到 stackoverflow?
是否因为在调用 sumArray1() 方法时请求了新的内存块?
当作为参数传递给方法时,数组不是通过指针访问的吗?

正如这里推荐的, 导致堆栈溢出的巨型数组,我总是使用 std::vector 并且从不在堆栈上分配大数组以防止上述问题。 如果有人知道可以使 sumArray1() 调用工作的任何调整、技巧或解决方法,我将不胜感激。

所以在我看来,你在编译时没有启用优化,否则main的数组将被完全优化,因为用它完成的计算从未以任何可观察的方式使用。 (这对任何事情都不利,真的。即使出于调试目的,您也应该使用-Og 。)

在您提到的评论中,您正在使用-fPIC标志进行编译。 这可以防止 gcc 内联sumArray1调用,因此无论优化标志如何,都无法优化数组。 您可能应该将-fpie (可能已经是默认值)用于可执行文件而不是-fPIC ,后者用于共享库并带有这些性能损失,另请参阅此处此处

如果是这种情况,那么回答您的问题:问题不在于传递给函数。 问题是堆栈空间是在进入函数时分配的,所以在设置限制之前。

现在这里的“分配”只是意味着修改堆栈指针,但很有可能在设置限制之前main访问了这个堆栈帧中的任何地方。 特别是编译器可以根据需要重新排序堆栈帧中变量的位置,或者它可能会根据您的编译器设置等添加堆栈保护。

在设置限制之前的任何此类访问都会导致分段错误。

请注意,编译器也可以自由地内联函数。 因此,如果编译器决定内联该函数调用,即使在sumArray0执行操作也可能会给您带来麻烦,因为这样数组将成为main堆栈帧的一部分,并具有与上述应用相同的问题。

编译器可能会认识到内联具有大堆栈帧的函数具有潜在危险,但不会这样做,但这是您需要在编译器文档中进行检查的内容。

在任何情况下,编译器和操作系统都不希望程序使用大型堆栈帧。 那不是他们的目的。 堆/空闲存储专门用于处理比通常堆栈帧大的内存分配。 为大型堆栈帧启用警告并注意它们通常是一种很好的做法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM