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 44つの異なる場所に行きました。では、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)は 3 つのシーケンス ポイントがあります。A で operator[] が呼び出されたとき、rec が呼び出されたとき、および最後に。ただし、最初の 2 つの順序は指定されていません。そのため、g++A[i]が最初に計算してから を呼び出しrec(idx+1)た場合、 が返されたときに、ベクトルの内部メモリの再割り当てによってrec返された参照が無効になる可能性があります。A[i]VC++ では、最初に評価される可能性があるrec(idx+1)ため、すべてのpush_back呼び出しが前もって行われます。つまり、A[i]呼び出しは正しいメモリ ブロックを参照します。あるいは、同じように動作する可能性がありますが、たまたま segfault が発生しません。これは、未定義の動作の問題の 1 つです。

に変更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 に答える