web-dev-qa-db-ja.com

std :: vectorを使用すると、&vec [0]の動作が未定義ですが、vec.data()は安全ですか?

私はFAQ at isocpp.org at "ここにリンク" を読んでいて、_std::vector_に関する警告に遭遇しました:

_std::vector<int> v;
auto a = &v[0]; // Is undefined behaviour but
auto a = v.data(); // Is safe
_

実際のサイトから:

_void g()
{
  std::vector<Foo> v;
  // ...
  f(v.begin(), v.size());  // Error, not guaranteed to be the same as &v[0]
    ↑↑↑↑↑↑↑↑↑ // Cough, choke, gag; use v.data() instead
}
_

また、_&v[0]_の使用は、_std::vector_または_std::array_が空の場合の未定義の動作ですが、.data()関数を使用しても常に安全です。

これを正確に理解したかどうかはわかりません。 ::data()は配列の先頭へのポインタを返し、_&[0]_は先頭のアドレスを返します。私はここで違いを見ていませんし、_&[0]_が何かを逆参照しているとは思いません(つまり、要素0のメモリを読み取っていません)。 Visual Studioのデバッグビルドで添え字_[0]_にアクセスするとアサーションが失敗しますが、リリースモードでは何も表示されません。また、どちらの場合も、デフォルトで作成されたベクトルのアドレスは0です。

また、::begin()についてのコメントが_::operator[0]_と同じであるとは限りません。ベクトルの場合、begin()イテレータ、::data()、および_&[0]_の生のポインタはすべて同じ値であると想定しました。

25
Zebrafish

私はここで違いを見ていません

_&v[0]_は&(v[0])と同じです。つまり、vの最初の要素からアドレスを取得します。しかし、vが空の場合、要素はまったくありません。_v[0]_はUBにつながるだけで、存在しない要素を返そうとします。そこからアドレスを取得しようとしても意味がありません。

v.data() は常に安全です。基になる配列へのポインタを直接返します。 vが空の場合でも、ポインターは有効です(nullポインターかそうでない可能性があります)。ただし、それを逆参照すると(*v.data()など)、UBにもつながります。これは、_v[0]_と同じです。

また、::begin()に関するコメントがわかりません。_::operator[0]_と同じであるとは限りません。

_std::vector::begin_ は、タイプ _std::vector::iterator_ のイテレータを返します。これは、 RandomAccessIterator の要件を満たす必要があります。それは生のポインタかもしれませんが、そうである必要はありません。クラスとして実装することは許容されます。

30
songyuanyao

あなたの例を理解しやすくするためにあなたの質問に欠けている情報は、Fooのベクトルでvoid f(Foo* array, unsigned numFoos);を呼び出す.begin()ポインタ。しかし、一部の実装は、機能するのに十分な動作をする場合があります。

空のベクターの場合、v.data()はポインターを返しますが、それが何を指しているのかわかりません。 nullptrの可能性もありますが、これは保証されていません。

2
Bo R

それはすべて1つの単純なことに帰着します:ポインターに整数値を加算または減算できますが、無効なポインターを逆参照しようとすることは未定義の動作です。

たとえば、

_int a[10];
int* p = a;
int* q = p + 10;   // This is fine
int r = *(p + 10)  // This is undefined behaviour
_

あなたの例では:_v[0]_は*(v's internal pointer+0)と同じであり、これはベクトルが空の場合の問題です。

2
StPiere