如何调试具有许多小静态数组的长if/else树中的堆栈溢出



我正在尝试编译和运行别人编写的c++项目。这在MacOS和linux中有效,但在使用MS Visual Studio 2022的Windows上由于堆栈溢出而失败。问题在于一个巨大的函数,它在if-else树中有成千上万的情况,其中内部嵌套有各种多维静态数组。例如,类似这样的东西,但4500行长:

void foo(int mode) {
if (mode == 0) {
int vals[2][5];
...
}
else if (mode == 1) {
int vals[3][6][2];
...
}
...
}

gcc和MS Visual Studio等典型编译器将如何为这种类型的代码模式分配堆栈空间?

在不启用优化的情况下进行编译时,Visual Studio似乎会为每个变量分配单独的堆栈空间(这并非不合理,如果在执行函数时一个变量不会突然变为另一个变量,则调试会更容易(。在发布模式下编译时,它会优化未使用的数组,并只分配所需的堆栈空间。

即使没有启用优化,GCC似乎也不会为每个阵列分配单独的堆栈空间。此外,macos和Linux上的默认堆栈大小通常为8MB,而在Windows上通常为1MB。

因此,解决问题的最简单方法可能是启用编译器优化。更好的解决方案是重构代码,使其结构更好。

作为参考,这是我用于测试的代码:

#include <iostream>
void foo(int mode) {
if (mode == 1)
{
int foo[200'000];
std::cout << foo[mode];
}
else if (mode == 2)
{
int foo[200'000];
std::cout << foo[mode + 2];
}
else if (mode == 3)
{
int foo[200'000];
std::cout << foo[mode + 3];
}
}
int main()
{
int mode;
std::cin >> mode;
foo(mode);
}

在调试模式下,Visual Studio只允许80000个元素(80000*4(sizeof(int)(*3~=960kB(。

在释放模式下,它允许200000个元素(200000*4~=800kB(。

Linux上的GCC允许2000000个元素(2000000*4~=8MB(。

开关&静态

我的快速建议是使用switchstatic:

void Bar(const int mode)
{
switch (mode)
{
case 0:  
{
static int vals[2][5];
//...
}
break;
}
}

使用static关键字将分配移动到另一个内存段,该内存段的容量通常大于本地(堆栈(区域的容量。一个副作用是,无论代码是否通过案例,所有这些数组分配都将存在。

函数指针表

另一种方法是使用<mode, function-pointer>的表格。您也可以使用std::map

typedef void (*Function_Pointer)();
void Do_Mode_0();
void Foo(const int mode)
{
static std::map<int, Function_Pointer> delegation_table;
static bool is_initialized = false;
if (!is_initialized)
{
is_initialized = true;
delegation_table[0] = Do_Mode_0;
//...
}
// Execute the function
Function_Pointer p_function = delegation_table[mode];
(*p_function)(); // Execute the function associated with the mode.
}

这将使您的函数(在所有情况下(变得更小。您还可以将每个函数放在一个单独的文件中,并且每个文件可以有不同的方法来分配数组。

静态&函数指针构造表

我更喜欢使用static const表格。因为该表是staticconst,所以可以在编译时初始化该表。

struct Delegation_Entry
{
int key;
Function_Pointer p_function;
};
static const Delegation_Entry delegation_table[] =
{
{ 0, Do_Mode_0},
//...
};
static const table_size =
sizeof(delegation_table) / sizeof(delegation_table[0];
void Fuzz(const int mode)
{
for (int i = 0; i < table_size; ++i)
(
if (delegation_table[i].mode == mode)
{
Function_Pointer p_function = delegation_table[i].p_function;
(*p_function)();
break;
}
}
// Perform error handling if necessary
}

您可以添加更多";案例";在不测试代码的情况下将其添加到表中;因为代码是数据驱动的。

最新更新