将大堆栈分配数组作为函数参数传递会导致堆栈溢出
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;
}
我的问题是:
为什么我在第一种情况下会得到 Whosebug?
是不是调用sumArray1()方法时请求了一块新的内存?
当作为参数传递给方法时,数组不是通过指针访问的吗?
正如这里所推荐的那样,Giant arrays causing stack overflows,我总是使用 std::vector 并且从不在堆栈上分配大数组以防止出现上述问题。如果有人知道可以调用 sumArray1() 的任何调整、技巧或解决方法,我将不胜感激。
所以在我看来你是在没有启用优化的情况下进行编译,因为否则 main
中的数组将被完全优化掉,因为用它完成的计算从未以任何可观察的方式使用。 (这对任何事情都不利,真的。即使出于调试目的,您也应该使用 -Og
。)
在评论中您提到您正在使用 -fPIC
标志进行编译。这可以防止 gcc 内联 sumArray1
调用,因此无论优化标志如何,都无法优化数组。您可能应该对可执行文件使用 -fpie
(可能已经是默认值)而不是 -fPIC
,后者用于共享库并会带来这些性能损失,另请参阅 and 。
如果是这样,那么回答你的问题:问题不在于传递给函数。问题是堆栈 space 是在函数进入时分配的,所以在设置限制之前。
现在这里的"allocating"只是意味着修改栈指针,但是很有可能这个栈帧中的任何地方在设置限制之前被main
访问。特别是编译器可以根据需要重新排序堆栈帧中变量的位置,或者它可以根据您的编译器设置等添加堆栈保护。
在设置限制之前进行任何此类访问都会导致分段错误。
请注意,编译器也可以自由地内联函数。因此,如果编译器决定内联该函数调用,即使在 sumArray0
中执行操作也可能会给您带来麻烦,因为这样数组将成为 main
堆栈框架的一部分,并会出现与上述应用相同的问题.
编译器可能会认识到内联具有大堆栈帧的函数存在潜在危险,因此不会这样做,但您需要在编译器文档中检查这一点。
无论如何,编译器和操作系统都不希望程序使用大堆栈帧。那不是他们的目的。 heap/free 存储专门用于处理比通常的堆栈帧更大的内存分配。为大型堆栈帧启用警告并注意它们通常是一种很好的做法。
我在使用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;
}
我的问题是:
为什么我在第一种情况下会得到 Whosebug?
是不是调用sumArray1()方法时请求了一块新的内存?
当作为参数传递给方法时,数组不是通过指针访问的吗?
正如这里所推荐的那样,Giant arrays causing stack overflows,我总是使用 std::vector 并且从不在堆栈上分配大数组以防止出现上述问题。如果有人知道可以调用 sumArray1() 的任何调整、技巧或解决方法,我将不胜感激。
所以在我看来你是在没有启用优化的情况下进行编译,因为否则 main
中的数组将被完全优化掉,因为用它完成的计算从未以任何可观察的方式使用。 (这对任何事情都不利,真的。即使出于调试目的,您也应该使用 -Og
。)
在评论中您提到您正在使用 -fPIC
标志进行编译。这可以防止 gcc 内联 sumArray1
调用,因此无论优化标志如何,都无法优化数组。您可能应该对可执行文件使用 -fpie
(可能已经是默认值)而不是 -fPIC
,后者用于共享库并会带来这些性能损失,另请参阅
如果是这样,那么回答你的问题:问题不在于传递给函数。问题是堆栈 space 是在函数进入时分配的,所以在设置限制之前。
现在这里的"allocating"只是意味着修改栈指针,但是很有可能这个栈帧中的任何地方在设置限制之前被main
访问。特别是编译器可以根据需要重新排序堆栈帧中变量的位置,或者它可以根据您的编译器设置等添加堆栈保护。
在设置限制之前进行任何此类访问都会导致分段错误。
请注意,编译器也可以自由地内联函数。因此,如果编译器决定内联该函数调用,即使在 sumArray0
中执行操作也可能会给您带来麻烦,因为这样数组将成为 main
堆栈框架的一部分,并会出现与上述应用相同的问题.
编译器可能会认识到内联具有大堆栈帧的函数存在潜在危险,因此不会这样做,但您需要在编译器文档中检查这一点。
无论如何,编译器和操作系统都不希望程序使用大堆栈帧。那不是他们的目的。 heap/free 存储专门用于处理比通常的堆栈帧更大的内存分配。为大型堆栈帧启用警告并注意它们通常是一种很好的做法。