我正在编写一个代码,我使用 3 维 boost 多数组来保存坐标。但我总是在某个时候遇到分段错误。 boost 多数组大小如何受到限制以及如何绕过这些限制?
这是重现问题的简化测试代码:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <string>
#include <algorithm>
#include <map>
#include <boost/multi_array.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include "Line.h"
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
typedef struct {
Eigen::Vector3d coords;
int gpHostZone;
int gpHostFace;
int calculated;
} Vertex;
class LGR {
public:
LGR (int i, int j, int k) :
grid(boost::extents[i][j][k])
{
};
std::string name;
std::vector<int> hostZones;
std::vector<int> refine;
boost::multi_array<Vertex*, 3> grid;
std::vector<double> data;
};
int main(void){
LGR lgr(11,11,21);
std::cout << lgr.grid.size();
std::vector<LGR> v;
std::vector<Vertex> vertexDB;
for(int i = 0; i < 1; i++ ){
for(int j = 0; j < lgr.grid.size(); j++ ){
for(int k = 0; k < lgr.grid[0].size(); k++ ){
for(int l = 0; l < lgr.grid[0][0].size(); l++ ){
Vertex coord;
coord.coords << i,j,k;
coord.gpHostZone = 0;
coord.gpHostFace = 0;
coord.calculated = 0;
vertexDB.push_back(coord);
lgr.grid[j][k][l] = &(vertexDB.back());
}
}
}
for(int j = 0; j < lgr.grid.size(); j++ ){
for(int k = 0; k < lgr.grid[0].size(); k++ ){
for(int l = 0; l < lgr.grid[0][0].size(); l++ ){
std::cout << "At ("<< i << ","<< j << ","<< k << "," << l << ")\n";
std::cout << lgr.grid[j][k][l]->coords<<"\n\n";
}
}
}
}
return 1;
}
请不要对包含内容发表评论。我只是从实际代码中复制并粘贴。其中大部分可能在这里不需要。这些尺寸来自真实的示例,所以我实际上需要这些尺寸(可能还有更多)。
以下是导致未定义行为的明确问题,与
boost::multiarray
没有任何关系。
这些行:
std::vector<Vertex> vertexDB;
//...
vertexDB.push_back(coord);
lgr.grid[j][k][l] = &(vertexDB.back());
调整
vertexDB
向量的大小,然后将指向向量中最后一项的指针存储到 lgr.grid[j][k][l]
。这样做的问题是,由于向量在调整向量大小时必须重新分配内存,因此指向向量中项目的指针和迭代器可能会变得无效。
这会在随后的循环中体现出来:
std::cout << lgr.grid[j][k][l]->coords<<"\n\n";
不保证您之前分配的地址有效。
对此问题的快速解决方法是使用
std::list<Vertex>
,因为向 std::list
添加项目不会使迭代器/指针无效。
另一个解决方案正确诊断了原始代码中的问题(https://godbolt.org/z/Koc7sYEMY)。
一种解决方案(尽管很脆弱)是防止
push_back
使存储在数组中的指针无效。
考虑到你的程序的逻辑,这应该是:
LGR lgr(11,11,21);
std::cout << lgr.grid.size();
std::vector<LGR> v;
std::vector<Vertex> vertexDB;
vertexDB.reserve(lgr.grid.num_elements());
for(int i = 0; i < 1; i++ ){
...
https://godbolt.org/z/Yx5aYoTcG
这也将使
vertexDB
的人口更加高效。
一个更优雅的替代方案(仍然不完美)是保存指针间接寻址并直接处理向量数据的 array_ref (也需要保留)。
class LGR {
public:
LGR (int i, int j, int k, Vertex* data) :
// grid(boost::extents[i][j][k]),
grid_ref(data, boost::extents[i][j][k])
{
};
std::string name;
std::vector<int> hostZones;
std::vector<int> refine;
// boost::multi_array<Vertex*, 3> grid;
boost::multi_array_ref<Vertex, 3> grid_ref;
std::vector<double> data;
};
int main(void){
std::vector<Vertex> vertexDB;
vertexDB.reserve(11*11*21);
LGR lgr(11,11,21, vertexDB.data());
...