C++:当我添加看似无关的代码行时,分段错误消失了
Posted
技术标签:
【中文标题】C++:当我添加看似无关的代码行时,分段错误消失了【英文标题】:C++: Segmentation Fault Goes Away When I Add a Seemingly Unrelated Line of Code 【发布时间】:2012-11-16 09:38:48 【问题描述】:我正在使用 C++ 中的指针向量和迭代器。我最初编写它的方式会导致段错误,但是通过看似微不足道的更改,声明和初始化一个未使用的变量,段错误就会消失。有谁知道为什么?
这是段错误的代码。成功执行的最后一行是第 8 行(通过 printf 语句找到),取消注释的第 4 行摆脱了段错误:
1 Intersect RayTracer::closestShape(Ray r)
2 vector<Shape *>::iterator itStart = scene.getShapes().begin();
3 vector<Shape *>::iterator itEnd = scene.getShapes().end();
4 //vector<Shape *> sceneShapes = scene.getShapes(); This is the unused line that will cause the code to run successfully if I uncomment it.
5 Intersect closest = Intersect();
6 for(;itStart != itEnd; itStart++)
7 Intersect currentIntersect = (*itStart)->intersect(r);
8 if(currentIntersect.isHit())
9 if(currentIntersect.getT() < closest.getT())
10 closest = currentIntersect;
return closest;
这是不再出现段错误的工作版本:
1 Intersect RayTracer::closestShape(Ray r)
2 vector<Shape *> sceneShapes = scene.getShapes();
3 vector<Shape *>::iterator itStart = sceneShapes.begin();
4 vector<Shape *>::iterator itEnd = sceneShapes.end();
5 Intersect closest = Intersect();
6 for(;itStart != itEnd; itStart++)
7 Intersect currentIntersect = (*itStart)->intersect(r);
8 if(currentIntersect.isHit())
9 if(currentIntersect.getT() < closest.getT())
10 closest = currentIntersect;
return closest;
如果有人能解释为什么会发生这种情况,将不胜感激!请让我知道是否可以添加任何内容来澄清我的问题。
【问题讨论】:
vector<Shape *>::iterator itStart = scene.getShapes().begin();
创建一个在语句结束时终止的临时变量,因此它的迭代器不是很好。
如果可以,请在 Valgrind 等工具中运行此代码。它将通过告诉您内存损坏的位置来帮助您,这通常是这种“添加无意义的额外变量修复崩溃”症状的根本原因。
你知道你在复制向量吗?在第一个版本中,您获得向量副本的 begin(),然后您获得向量不同副本的 end()。在第二个版本中,您会得到一个副本……但是复制向量不是一个好主意;使用参考。此外,您完全错误地陈述了这些代码示例之间的区别......第二个声明,初始化,然后在两个语句中使用您的“未使用”变量。奇怪的是,它只是添加了“无关”的代码行。
【参考方案1】:
vector<Shape *> sceneShapes = scene.getShapes();
在堆栈上创建持久对象。
itStart
和 itEnd
指向有效内存。在您的第一个示例中,迭代器指向无效内存,因为它们指向来自调用 scene.getShapes()
的临时对象,该对象已立即被销毁并使您的迭代器无效。
当您取消注释您的//vector<Shape *> sceneShapes = scene.getShapes();
行时,它会返回与临时相同的内存边界的向量,并且迭代器再次有效!但也不是 100% 的可能性相同,您必须非常小心,避免出现此类问题。
【讨论】:
太棒了。谢谢您的回答。这种解释对于第二个例子来说是完全合理的。但是,我仍然有点困惑,为什么当我取消注释第 4 行时第一个示例可以正常工作而没有任何错误,即使sceneShapes
的初始化发生在迭代器初始化之后。【参考方案2】:
这个函数:
scene.getShapes()
可能是按值返回,这意味着当您在第一个示例中调用 begin()
和 end()
时,您是在尚未绑定的临时对象上调用它们。
如果您无法将函数更改为通过引用返回,您可以绑定一个 const 引用,然后在 const 引用上调用 begin()
和 end()
你的修复)。
如果您可以将函数更改为通过引用返回,那就更好了。理想情况下是 const 引用。
注意:返回 const 引用意味着您不能调整向量的大小或更改其中的指针,但指针指向的内容仍然可以更改。这些是指向 Shape 的指针。
【讨论】:
【参考方案3】:由于已经回答了崩溃的原因,让我尝试回答为什么取消注释您提到的行可能会导致崩溃消失。从函数返回的临时未命名向量将存储在堆栈上,其中将包含指向堆上后备存储的指针(对于在您的情况下是指向 Shape 对象的指针的元素),并且可能很少有人提及大小或结束等等。
当迭代器指向后备存储(开始和结束)不再有效时,就会出现问题,因为临时向量将在语句结束时被销毁(实际上是 2 次),并且与它一起的后备存储内存(包含指向 Shape 对象的指针)返回给堆管理器。当在堆栈上创建命名向量对象时(当您取消注释已注释掉的行时),向量代码可能会从堆管理器中获取相同的内存块 - 未命名向量得到 - 通过制作迭代器(指针)有效,因此避免了崩溃。在没有命名向量的情况下,堆管理器最终可能会将块分解成碎片或将其与大块组合并分配给其他可能随后调用的调用者。
【讨论】:
以上是关于C++:当我添加看似无关的代码行时,分段错误消失了的主要内容,如果未能解决你的问题,请参考以下文章