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

时间:2019-12-10 18:44:22

标签: c++ arrays linux c++17 stack-overflow

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

// with parsed JSON to Object in hands
Class<?> clazz = jsonObject.getClass();

for(Field field : clazz.getDeclaredFields()) {
    if(!field.getType().getName().equals("String"))
        break;

    field.setAccessible(true);
    String fieldValue = field.getValue().toString();

    if(fieldValue.startsWith("${"))
        field.set(jsonObject, fieldValue.replace("${", "").replace("}", ""));
    else
        // desired treatment
}

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

按照这里Giant arrays causing stack overflows的建议,我总是使用std :: vector,并且从不在堆栈上分配大数组以防止出现上述问题。如果有人知道可以使对sumArray1()的调用起作用的任何调整,技巧或变通办法,我将不胜感激。

1 个答案:

答案 0 :(得分:2)

在我看来,您是在未启用优化的情况下进行编译的,因为否则main中的数组将被完全优化,因为对其进行的计算从未以任何可观察的方式使用。 (这实际上对任何事情都是有害的。即使出于调试目的,您也可能应该使用-Og。)

在注释中,您提到您正在使用-fPIC标志进行编译。这样可以防止gcc内联sumArray1调用,从而无论优化标志如何都无法优化数组。您可能应该将-fpie(可能已经是默认值)用于可执行文件,而不是-fPIC(这是针对共享库的,并伴随这些性能损失),另请参阅here和{ {3}}。

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

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

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

请注意,编译器还可以自由使用内联函数。因此,即使编译器决定内联该函数调用,即使在sumArray0中执行操作也可能会给您造成麻烦,因为该数组将成为main堆栈框架的一部分,并且存在与上面讨论的相同的问题

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

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