12

考虑以下代码。

#include <stdio.h>
#include <vector>
#include <iostream>

struct XYZ { int X,Y,Z; };
std::vector<XYZ> A;

int rec(int idx)
{

   int i = A.size();
   A.push_back(XYZ());
   if (idx >= 5)
     return i;

   A[i].X = rec(idx+1);

   return i;
}

int main(){
  A.clear();
  rec(0);
  puts("FINISH!");

}

我无法弄清楚代码在 Linux(使用的 IDE:Code::Blocks)上出现分段错误的原因,而在 Windows(使用的 IDE:Visual C++)上却没有。

当我使用 Valgrind 来检查实际问题时,我得到了这个输出

我去了Invalid write of size 4四个不同的地方。那为什么我使用Visual C++时代码没有崩溃呢?

我错过了什么吗?

4

3 回答 3

17

rec()为向量赋值时,递归调用可能会修改向量。

如果更换会发生什么

A[i].X = rec(idx+1);

int tmp = rec(idx+1);
A[i].X = tmp;

?

另外,总结一下有用的注释:操作的操作数评估顺序=是未指定的,并且由于未预先分配向量,因此在递归调用期间可能会发生多次调整大小rec(),从而使向量中值的任何迭代器无效。

于 2010-05-06T06:31:20.877 回答
1

当我运行该代码时,我得到“ * 对象 0x300180 的错误:已释放对象的校验和不正确 - 对象可能在被释放后被修改。* ”。

我记得,A[i].X = rec(idx+1)有三个序列点。当在 A 上调用 operator[] 时,当调用 rec 时,以及最后。但是前两个的顺序是未指定的。因此,如果 g++A[i]先计算,然后调用rec(idx+1),那么当rec返回时,返回的引用A[i]可能会因向量内部内存的重新分配而失效。在 VC++ 下,它可能会先求值rec(idx+1),所以所有push_back调用都是预先完成的,这意味着A[i]调用引用了正确的内存块。或者,它可能会以同样的方式做事,而你只是碰巧没有出现段错误……这是未定义行为的问题之一。

更改std::vector<XYZ> A;std::vector<XYZ> A(10);将为 10 个元素保留足够的空间。这可以防止您的特定实现rec需要重新分配,并且可以解决我的错误。

于 2010-05-06T06:44:40.327 回答
0

你正在使用int i = A.size()

然后你将你的结构索引为一个数组,但使用大小值。您需要将其减少 1 例如A[i-1].X = rec(idx+1);

啊,我的错误- 我没有考虑向量 push_back。

于 2010-05-06T06:32:52.923 回答