C++容器的静态常量初始化列表导致堆栈溢出

3

我有一个静态的常量变量,是一个std::vector,如下所示:

std::vector<std::pair<GUID, std::array<double, 13>>>

我也尝试过(理论上向量占用更少的内存):

std::unordered_map<GUID, std::array<double, 13>, HashGUID >
std::map<GUID, std::array<double, 13>, GUIDComparer >

我在程序开始时使用初始化列表将其初始化,大约使用了5400个项。我知道这似乎有点大,但这并不算奇怪。这只是一个临时/中间解决方案。
然而,它总是抛出以下错误:
0xC00000FD:堆栈溢出(参数:0x00000000,0x00052000)。
如果我将列表保持较小,约为4000,则似乎可以正常工作,但我的完整列表5400就无法正常工作。有任何想法吗?
编辑: 这是我的初始化方式(其中4000左右的行都很好用。超过5400就不行):
static const std::vector<std::pair<GUID, std::array<double, 13>>> engVals={
    {{0x58341899, 0x8844, 0x3333, { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}}, {11, 101, 1.50, 3.50, 225.0, 850.0, 125.0,0.55, 19, 175, 565, 1.2, 0.44}},
    {{0x67633448, 0x8103, 0x3333, { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}}, {11, 102, 1.50, 3.50, 475, 1300.0, 275.0, 0.55, 19, 175, 565, 1.2, 0.44}},
    {{0x94422980, 0x6497, 0x3333, { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}}, {11, 103, 1.50, 3.50, 875.0, 1600.0, 500.0, 0.55, 19, 175, 565, 1.4, 0.51}},
...
};

编辑2 忘记说明,我在使用vs2013。这段代码实际上在编译为dll的库中。

我需要一个初始化列表,因为上面的初始化将由另一个应用程序生成,所以我需要一种一行方式来初始化这个容器。


1
当您将其更改为“数组”时,您将不再使用“initializer_list”,因此错误可能会消失。 - dyp
1
每个元素占用134 + 18=60字节,*5.4k = 324k的数据。另外,为什么要存储在vector中而不是array中? - Yakk - Adam Nevraumont
2
"使用vs2013" 噢,差点以为是这样。由于它不支持 constexpr,我怀疑它需要在运行时(初始化时)构造 initializer_list,因此需要堆栈空间。 - dyp
2
@mateuscb 更多的内存使用。如果你只需要512kb(对于一个典型的程序来说是合理的),而你使用了4mb,那么你有3.5mb未被使用,供你的程序余下部分使用。看起来似乎不是很多,但如果你的电脑上每个程序都这样做,并且你同时打开了许多程序呢? - Neil Kirk
1
@mateuscb:你一直说它必须在初始化列表中,因为它是由另一个程序生成的,但这并不成立。只需让其他程序生成二进制文件,并从中加载向量即可。完全绕过了问题。http://ideone.com/MrVOEe - Mooing Duck
显示剩余20条评论
3个回答

1

先尝试使用@dyp建议的std::array

如果不行,您可以尝试以下解决方法。我只是做了一个简单的例子。

std::vector<int> CreateVector()
{
    std::vector<int> temp;
    temp.reserve(3);
    temp.push_back(1);
    temp.push_back(2);
    temp.push_back(3);
    return temp;
}

static const std::vector<int> data = CreateVector();

谢谢,但我确实需要仍使用初始化列表的解决方案。我在问题中没有指定这一点。 - mateuscb
2
@mateuscb:我仍然不相信你需要一个初始化列表。 - Mooing Duck

1

我认为这里的问题在于初始化列表本身,因为在调用外部向量的构造函数时,它被推送到堆栈上。

我的建议是回到普通的数组,并将其设置为文件静态或全局常量。

struct GUI_xxx {
    GUID guid;
    double values[13];
};

const GUI_xxx engVal[] = {
     {{0x58341899, 0x8844, 0x3333, { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}}, {11, 101, 1.50, 3.50, 225.0, 850.0, 125.0,0.55, 19, 175, 565, 1.2, 0.44}},
     {{0x67633448, 0x8103, 0x3333, { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}}, {11, 102, 1.50, 3.50, 475, 1300.0, 275.0, 0.55, 19, 175, 565, 1.2, 0.44}},
     {{0x94422980, 0x6497, 0x3333, { 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}}, {11, 103, 1.50, 3.50, 875.0, 1600.0, 500.0, 0.55, 19, 175, 565, 1.4, 0.51}},
     ...  
};

你在技术上仍将使用初始化列表语法,但是(假设GUID足够简单),这可能会被优化为库的全局常量部分中的常量表。如果你仍然遇到类似的问题,甚至可以尝试在单独的.c文件中将此常量创建为外部C常量。(假设GUID是POD。)

真的很想要那个地图功能。我增加了堆栈大小。目前似乎可以工作。 - mateuscb
@mateuscb: std::array + std::lower_bound@mateuscb:std::array + std::lower_bound - Mooing Duck

1
你一直说它必须在初始化列表中,因为它是由另一个程序生成的,但这是站不住脚的。只需让其他程序生成二进制文件,并从中加载向量即可。这完全绕过了问题。
std::istream& operator>>(std::istream& in, std::pair<GUID, std::array<double, 13>>& var)
{return in.read((char*)&var, sizeof(var));}

std::vector<std::pair<GUID, std::array<double, 13>>> loadGUIDs()
{
    std::vector<std::pair<GUID, std::array<double, 13>>> ret;
    ret.reserve(5400); //or sizeof file / 120b or whatever
    std::ifstream infile("GUIDs.bin");
    std::istream_iterator<std::pair<GUID, std::array<double, 13>>> begin(infile),end;
    ret.assign(begin,end);
    return ret;
}
static const std::vector<std::pair<GUID, std::array<double, 13>>> engVals = loadGUIDs();

http://ideone.com/MrVOEe

关于用户编辑的担忧,外部二进制文件并不比可执行文件本身更容易编辑。此外,这种方法还有一个副作用,即可以单独更新它们,编译时间更快等等。


1
我对这个答案进行了小修改,不是将文件存储在打开的位置,而是将其作为资源添加。这并不会使它更难,但至少它被封装在EXE中。 - mateuscb
1
实际上,现在我睡过一晚后,你应该将其读入到boost::flatmapstd::unordered_map而不是std::vector中。这会使用更多的内存,但是然后你可以在O(1)时间内执行std::array<double, 13>& data = engVals[myGUID] - Mooing Duck
没错,我使用了std::unordered_map!再次感谢您的所有帮助! - mateuscb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接