在大数组C++堆叠溢出



嗯,我正在为大学编写一个程序,我必须将数据转储转换为HDF格式。数据转储如下所示:

"1444394028","1","5339","M","873"
"1444394028","1","7045","V","0.34902"
"1444394028","1","7042","M","2"
"1444394028","1","7077","V","0.0470588"
"1444394028","1","5415","M","40"
"1444394028","1","7077","V","0.462745"
"1444394028","1","7076","B","10001101"
"1444394028","1","7074","M","19"
"1444394028","1","7142","M","16"
"1444394028","1","7141","V","0.866667"

对于HDF5 API,我需要一个数组。所以我现在的方法是,将数据转储写入这样的数组中:

int count = 0;
std::ifstream countInput("share/ObservationDump.txt");
std::string line;
if(!countInput) cout << "Datei nicht gefunden" << endl; 
while( std::getline( countInput, line ) ) {
count++;
}
cout << count << endl;
struct_t writedata[count];
int i = 0;
std::ifstream dataInput("share/ObservationDump.txt");
std::string line2;
char delimeter(',');
std::string timestampTemp, millisecondsSinceStartTemp, deviceTemp, typeTemp, valueTemp;
while (std::getline(dataInput, timestampTemp, delimeter) ) 
{
std::getline(dataInput, millisecondsSinceStartTemp, delimeter);
std::getline(dataInput, deviceTemp, delimeter);
std::getline(dataInput, typeTemp, delimeter);
std::getline(dataInput, valueTemp);
writedata[i].timestamp = atoi(timestampTemp.substr(1, timestampTemp.size()-2).c_str());
writedata[i].millisecondsSinceStart = atoi(millisecondsSinceStartTemp.substr(1, millisecondsSinceStartTemp.size()-2).c_str());
writedata[i].device = atoi(deviceTemp.substr(1, deviceTemp.size()-2).c_str());
writedata[i].value = atof(valueTemp.substr(1, valueTemp.size()-2).c_str());
writedata[i].type = *(typeTemp.substr(1, typeTemp.size()-2).c_str());
i++; 
}

struct_t定义为

struct struct_t
{
int timestamp;
int millisecondsSinceStart;
int device; 
char type; 
double value; 
};

正如你们中的一些人可能看到的那样,对于大数据转储(大约 60,000 行),数组writedata往往会产生堆栈溢出(分段错误)。我需要一个数组来传递它到我的 HDF 适配器。如何防止溢出?我无法通过广泛的谷歌搜索找到答案。提前感谢!

您遵循的示例代码是 C 语言,而您正在编写的代码是 C++。在大多数情况下,有效的 C 代码C++代码是有效的,尽管不一定是好的样式;这是它不是的时候之一,尽管由于这不是你真正的问题,我将在我的答案末尾留下解释。

当您声明struct_t writedata[count];时,您将在堆栈上创建一个数组。堆栈的大小通常被人为限制,因此在堆栈上创建大型数组可能会导致堆栈空间不足的问题。这就是你所看到的。典型的解决方案是在堆中创建大型数据结构(尽管堆的主要用途是使数据持续超过创建它的函数的return)。

访问堆的最C++惯用方法是不要直接执行此操作,而是使用帮助程序容器类。在这种情况下,您想要的是std::vector,它允许您将数据推送到末尾,并且会随着您推送更多数据而自动增长。由于它会自动增长,因此您无需提前指定大小;只需将其声明为std::vector<struct_t> writedata;(阅读"std::vectorofstruct_t")。同样,由于它不需要提前知道大小,因此您也可以忽略整个第一个循环。

向量最初是空的;要将数据放入其中,通常需要使用writedata.push_back()writedata.emplace_back()。其中第一个采用现有的struct_t;第二个采用用于创建参数的参数。所有元素都连续存储在内存中,就像在 C 数组中一样,您可以使用writedata.data()直接访问该数组。

在函数结束时,当vector超出范围且不再可访问时,将调用其析构函数并自动清理它使用的内存。


另一种选择,而不是使用std::vector,是自己管理内存。这样做C++方法是newdelete.处理这个问题的最简单方法是仍然像您一样计算count,但不是通过仅将其声明为count大小的数组来在堆栈上创建数组,而是struct_t* writedata = new struct_t[count];。这将在堆中创建countstruct_t的数组,并将writedata设置为指向此数组的第一个元素的指针。然后,您可以像在程序中使用数组一样使用它,但由于它在堆上,因此不会耗尽堆栈空间。

这样做的缺点是您需要提前知道大小,并且需要清理自己使用的内存。为此,当您不再需要数据时,应运行delete[] writedata。之后,writedata仍将指向内存中的同一位置,但您的程序不再拥有该数据,因此您需要确保永远不再使用该值;标准方法是在删除后立即将writedata设置为nullptr

您还可以使用 C 等价物来newdelete,它们是mallocfree。在您的情况下,它们大多是等效的,但对于更复杂的示例,您应该记住,这些使内存未初始化,而newdelete将运行您创建的构造函数/析构函数,以确保对象在开始时处于理智状态,并且不会在结束时留下资源。


现在,对于为什么您的原始代码实际上对任何大小的文件都无效C++:您的行struct_t writedata[count];尝试创建一个countstruct_t数组。由于count是一个变量,因此称为可变长度数组(VLA)。这些事情在较新版本的 C 中是合法的,但在C++中不是。只要您只想在当前使用的同一系统上编译代码,仅此一项就值得警告,因为您的编译器似乎支持 VLA 作为扩展。但是,如果要在任何其他系统上编译代码(使其更具可移植性),则不应使用这样的编译器扩展。

struct_t writedata[count];

这个数组被分配到通常非常小的堆栈上,当它达到一个太大的值(这是半任意的)时,这将溢出堆栈。 您最好通过执行以下操作来分配堆:

struct_t* writedata = (struct_t*)malloc(sizeof(struct_t) * count);

然后在完成内存后添加相应的调用以释放,例如

free(writedata);
writedata = nullptr;

最佳做法是检查 while 循环中的i < count,就好像您注销数组的末尾一样 可能会发生坏事。

相关内容

  • 没有找到相关文章

最新更新