将值推送到 std::vector 中,但似乎在错误的地址

pushed value into std::vector but seems to be at wrong address

提问人:Joao Pincho 提问时间:8/23/2022 更新时间:8/23/2022 访问量:65

问:

我有这段代码,从 Assimp aiNode 树转换为我自己的树数据结构。这是一个非常简单的递归函数,它遍历树,用节点数据填充结构,并为每个子节点调用自身。唯一的区别是,我不是动态分配每个节点,而是将其推送到向量中,并使用索引而不是指针来连接它们。

int AddNode( ModelDescriptor &LoadedModelDescriptor, const int ParentIndex, const aiNode *CurrentAssimpNode )
{
int NewIndex = (int) LoadedModelDescriptor.NodeTree.size();
LoadedModelDescriptor.NodeTree.push_back( MeshTreeNode() );
LoadedModelDescriptor.NodeTree[NewIndex].Name.assign( CurrentAssimpNode->mName.C_Str() );
LoadedModelDescriptor.NodeTree[NewIndex].ParentNode = ParentIndex;
LoadedModelDescriptor.NodeTree[NewIndex].DefaultTransformationMatrix = glm::transpose( glm::make_mat4( (float *) &CurrentAssimpNode->mTransformation ) );
LOG_DEBUG("processing node %u %s %u meshes %u children", NewIndex, LoadedModelDescriptor.NodeTree[NewIndex].Name.c_str(), CurrentAssimpNode->mNumMeshes, CurrentAssimpNode->mNumChildren );
for ( unsigned MeshIterator = 0; MeshIterator < CurrentAssimpNode->mNumMeshes; ++MeshIterator )
    {
    LoadedModelDescriptor.NodeTree[NewIndex].MeshIndices.push_back( CurrentAssimpNode->mMeshes[MeshIterator] );
    }

for ( unsigned ChildIterator = 0; ChildIterator < CurrentAssimpNode->mNumChildren; ++ChildIterator )
    {
    LoadedModelDescriptor.NodeTree[NewIndex].ChildNodes.push_back( AddNode( LoadedModelDescriptor, NewIndex, CurrentAssimpNode->mChildren[ChildIterator] ) );
    }
LOG_DEBUG("finished processing node %u %s %u meshes %u children", NewIndex, LoadedModelDescriptor.NodeTree[NewIndex].Name.c_str(), LoadedModelDescriptor.NodeTree[NewIndex].MeshIndices.size(), LoadedModelDescriptor.NodeTree[NewIndex].ChildNodes.size());
return NewIndex;
}

以下是使用的相关结构:

struct MeshTreeNode
{
std::string Name;
glm::mat4 DefaultTransformationMatrix;
std::vector <uint32_t> MeshIndices;
std::vector <int> ChildNodes;
int ParentNode;
};

struct ModelDescriptor
{
std::string Name;
std::vector <MeshTreeNode> NodeTree;
std::vector <MeshDescriptor> Meshes;
std::vector <LightDescriptor> Lights;
BoundingBox BBox;
};

没什么,没有隐藏的东西。在 Windows 下可以完美运行,但昨晚在 Debian 上使用 GCC 运行它时遇到了一个令人讨厌的惊喜。调用后,NodeTree 已完全填充,但所有节点都没有填充的 ChildNodes 向量。 所以基本上,第一个节点被认为是没有子节点的,节点树就在那里结束了。 经过相当多的调试,我注意到一切都运行顺利,正在添加子节点,但由于某种原因,最终树的内容不同。我认为递归调用的行为不符合预期,就当时在内存中加载的内容而言。 于是我把这个小小的push_back电话分成了两行:

        int ChildNodeIndex = AddNode( LoadedModelDescriptor, NewIndex, CurrentAssimpNode->mChildren[ChildIterator] );
    LoadedModelDescriptor.NodeTree[NewIndex].ChildNodes.push_back( ChildNodeIndex );

基本上只是在推送索引之前暂时将索引存储在变量中,但现在它可以工作了。显然,该索引被推送到某个向量中,而该向量在push_back实际运行时不再有效。但根据我的理解,它绝对应该是,它应该是调用堆栈中的最后一件事,无论向量因为递归调用而在内部重新分配了多少次,它在堆栈展开期间仍然应该是准确的,因为我认为它只会将 std::vector 的“this”推送到堆栈上。

谁能告诉我为什么会这样?感觉不对劲。

C++ Linux GCC 标准

评论

0赞 PaulMcKenzie 8/23/2022
int NewIndex = (int) LoadedModelDescriptor.NodeTree.size();-- 也许是题外话,但如果你只是在 之后有 ,然后简单地在整个函数的其余部分使用,那就没有必要了。NewIndexauto& lastItem = LoadedModelDescriptor.NodeTree.back();push_back()lastItem
3赞 PaulMcKenzie 8/23/2022
但似乎在错误的地址 - 在代码的其他任何地方,您是否存储了要添加到向量中的项的地址?如果是,那么这很危险,因为这些地址将在调整矢量大小超过当前容量时失效。如果是这样,那么这就是你得到随机、不稳定行为的原因。
0赞 Joao Pincho 8/25/2022
@PaulMcKenzie我不能。想象一下,选择一个有子节点的节点(正如预期的那样)。根节点有子节点 1a、2a、3a。子节点 1a 还有另一个子节点 1b。主节点位于偏移量 0,子节点 1a 位于偏移量 1,子节点 1b 位于偏移量 2。1b 和 1c 将位于偏移量 3 和 4 处,但它们的父节点不是最新的,它实际上是第一个偏移量。
0赞 Joao Pincho 8/25/2022
@PaulMcKenzie我不存储地址,甚至不在这里。只有索引。我只能想象这个向量的大小调整会在堆栈上做一些事情,因为如果我像在 GCC 上一样运行这段代码,那么任何节点都没有任何子节点。仅当我应用上面提到的修复程序时。
0赞 PaulMcKenzie 8/26/2022
基本上只是在推送索引之前暂时将索引存储在变量中,但现在它可以工作了。-- 更改或添加实际上不执行任何操作的代码行清楚地表明程序的其他部分正在发生内存损坏。我建议你把原来的代码放回去,然后实际识别错误并解决它。

答: 暂无答案