C++ 为什么push_back会导致分割错误?

C++ 为什么push_back会导致分割错误?,c++,stl,segmentation-fault,stack,C++,Stl,Segmentation Fault,Stack,关于项目: 我正在开发一个Opengl光线跟踪器,它能够加载obj文件并对其进行光线跟踪。我的应用程序使用assimp加载obj文件,然后使用着色器存储对象将所有三角形面(顶点和索引)发送到片段着色器。基本结构将从片段着色器将结果渲染到四元体 当我加载更大的obj-s(超过100个三角形)时,计算机花了太多时间进行交叉,因此我开始创建BVH树以加快过程。My BVH基于AABB中包含的三角形面的平均中值递归地将空间拆分为两个轴对齐的边界框 我成功地构建了BVH树结构(在CPU上),现在我想将其转

关于项目:

我正在开发一个Opengl光线跟踪器,它能够加载obj文件并对其进行光线跟踪。我的应用程序使用assimp加载obj文件,然后使用着色器存储对象将所有三角形面(顶点和索引)发送到片段着色器。基本结构将从片段着色器将结果渲染到四元体

当我加载更大的obj-s(超过100个三角形)时,计算机花了太多时间进行交叉,因此我开始创建BVH树以加快过程。My BVH基于AABB中包含的三角形面的平均中值递归地将空间拆分为两个轴对齐的边界框

我成功地构建了BVH树结构(在CPU上),现在我想将其转换为一个简单的数组,然后将其发送到片段着色器(到着色器存储缓冲区)

下面是负责将BVH根节点转换为数组的方法:

BvhNode bvhNode; //global variable

BvhNode* putNodeIntoArray() {

    int size=bvhNode.getNumberOfNodes();
    BvhNode nodesArray[size];

    int current_index = 0;
    vector<BvhNode> tempNodes;

    tempNodes.push_back(bvhNode);
    BvhNode current_node;

    while (!tempNodes.empty()) {
        current_node = tempNodes.at(0);

        tempNodes.erase(tempNodes.begin());
        nodesArray[current_index] = current_node;

        if(!current_node.isLeaf)
        {
            tempNodes.push_back(current_node.children.at(0)); // Segmentation error occurs here!
            tempNodes.push_back(current_node.children.at(1));
        }

        current_index++;
    }

    return nodesArray;
}

更新1: 我根据评论更新了方法。所以我将返回值的类型改为vector,而不是BvhNode*。在将叶节点放入std::vector的过程中,该算法运行良好。所以,当它开始把图的最后一级放到向量上时,它给了我这个错误:

Program received signal SIGSEGV, Segmentation fault.
0x00007fbda4d69c01 in __GI___libc_free (mem=0x555be3a9dba0) at malloc.c:3123
3123    malloc.c: No such file or directory.
我设法将七个节点(表示树的所有深度级别,除了叶子级别)放入向量中。我也试着运行valgring,但实际上valgrind并没有给我任何错误,不像CLion

这是我修改过的方法。我评论了分段错误的位置和修复

    BvhNode bvhNode;

    vector<BvhNode> putNodeIntoArray() {

        int size=bvhNode.getNumberOfNodes();

    // FIX:  I modified the array into an std::vector
        vector<BvhNode> nodesArray(size);  

        int current_index = 0;
        vector<BvhNode> tempNodes;

        tempNodes.push_back(bvhNode);   
        BvhNode current_node;

        while (!tempNodes.empty()) {
            current_node = tempNodes.front();// Segmentation error!!
            tempNodes.erase(tempNodes.begin());

            nodesArray.at(current_index)=current_node;
            nodesArray.at(current_index).children.clear();


// FIX: I also modified this not to iterate through leaves' children, because they don't exist.
            if(!current_node.children.empty())  
            {
                tempNodes.push_back(current_node.children.at(0));
                tempNodes.push_back(current_node.children.at(1));
            }
            current_index++;
        }
        return nodesArray;
    }
BvhNode BvhNode;
向量putNodeIntoArray(){
int size=bvhNode.getNumberOfNodes();
//修复:我将数组修改为std::vector
向量节点阵列(大小);
int当前_指数=0;
向量节点;
tempNodes.push_-back(bvhNode);
BvhNode电流_节点;
而(!tempNodes.empty()){
当前节点=tempNodes.front();//分段错误!!
擦除(tempNodes.begin());
节点数组.at(当前索引)=当前节点;
nodesArray.at(当前_索引).children.clear();
//修正:我还修改了它,不遍历叶子的子元素,因为它们不存在。
if(!current_node.children.empty())
{
tempNodes.push_back(当前_节点.children.at(0));
tempNodes.push_back(当前_节点.children.at(1));
}
当前_索引++;
}
返回节点阵列;
}

您的向量按值存储
BvhNode
s。 这意味着每次
推回
一个节点时,都会调用它的复制构造函数,该构造函数反过来复制节点内的
子节点
向量成员,从而复制它自己的元素等。 这基本上会导致在每次插入或删除节点时复制/释放完整的子树

这反过来会导致内存碎片,最终导致向量重新分配失败并导致segfault

如果没有完整的代码,我可以推荐以下两件事:

  • 将子项存储为(智能)指针,而不是按值存储

  • 为向量创建自定义分配器以启用更细粒度的调试,并检查分配失败


  • 实际上,问题在于Bvh树的创建。因为我想以2*I+1和2*I+2的形式到达子级,所以我使用了一种方法使二叉树满(每个级别都有最大数量的节点)。在这种方法中,我必须将空节点推送到树上

    我是这样创建空节点的:

    BvhNode emptyNode;
    
    与此相反:

    BvhNode *emptyNode = new BvhNode();
    
    因此,当该方法完成时,
    emptyNode
    的生存期结束。这导致了分割错误


    此外,还有一个问题,正如Balázs Kovacsis指出的那样,我在创建平面二叉树的过程中按值存储了BvhNode。此选项使应用程序速度慢得多,这是另一个可能导致分段错误的地方。

    编译器不是因为返回指向堆栈分配数组的指针而对您大喊大叫吗?
    BvhNode nodesArray[size]——你使用的是代码>矢量,但是同时你使用了这个非标准的,不合法的C++语法。为什么不
    std::vector nodesArray(大小)?如果您这样做了,那么返回基于堆栈的数组的问题就不会成为问题。谢谢,我修改了返回值的类型,但不幸的是它是相同的,但要好得多。我用修改更新了这个问题。由于堆损坏问题已经众所周知地难以调试,请包含一个显示您的问题的实例。谢谢Balázs!是的,一直按价值复制不是一个好的选择。我用尽可能多的参考文献重写了整个方法。但是分割的错误在一个方法中,我使二叉树完整。我使用:bvhnodeempty将空节点添加到树中;而不是BvhNode*emptyNode=new BvhNode();因此,一旦方法完成,变量就被销毁了。
    
    BvhNode *emptyNode = new BvhNode();