0

Here T型の密接にパックされた連続した構造体メンバをT.TriviallyCopyable型Tの厳密にパックされたメンバがTの配列に詰め込まれている場合、逆の場合

の配列として扱うことは不適切です。

しかし、基本的な表現をコピーするのはどうですか?

は考える:同じ制約を持つ

struct vec { 
    float x, y, z; 
}; 

を:

static_assert(sizeof(vec) == 3 * sizeof(float)); 

は以下の通りです:

int main() { 
    vec v = {1.9f, 2.5f, 3.1f}; 

    float a[3]; 
    std::memcpy(&a, &v, 3 * sizeof(float)); 
    assert(a[0] == v.x); 
    assert(a[1] == v.y); 
    assert(a[2] == v.z); 

    vec u; 
    std::memcpy(&u, &a, 3 * sizeof(float)); 
    assert(u.x == a[0]); 
    assert(u.y == a[1]); 
    assert(u.z == a[2]); 
} 

法的?

+0

関連(または重複):http://stackoverflow.com/questions/37211298/accessing-an-array-as-a-struct-vs-undefined-behavior – Barmar

+0

@Barmarこれは、私はリンクされた質問。 –

+0

彼らはどちらも未定義の動作だと言っています。なぜあなたのケースは違うと思いますか? – Barmar

答えて

1

をfloatへの定数ポインタである、そのための明示的なサポートが標準でありませんそれに非常に近いもののサポートが推測できます。

は自明コピー可能タイプ Tを考えると、どのような明示的に許可されていますと、 char(または unsigned char)と背面の配列にその表現をコピーすることです。

配列の内容が配列自体に保存されている必要はありません。内容はファイルに保存され、プログラムのその後の実行時に再読み込みされる可能性があります。または、そのタイプが許す限り、異なるタイプのオブジェクトに格納されます。これを実行するには、同じ実行でタイプTのオブジェクトからそれらの表現が始まっていない場合、実装はmemcpyのオブジェクトへの表現を許可する必要があります。

その結果、非常に少なくとも、

int main() { 
    vec v = {1.9f, 2.5f, 3.1f}; 

    float a[3]; 

    assert(sizeof v == sizeof a); 

    { char tmp[3 * sizeof(float)]; 
     std::memcpy(tmp, &v, 3 * sizeof(float)); 
     std::memcpy(a, tmp, 3 * sizeof(float)); } 
    assert(a[0] == v.x); 
    assert(a[1] == v.y); 
    assert(a[2] == v.z); 

    vec u; 
    { char tmp[3 * sizeof(float)]; 
     std::memcpy(tmp, a, 3 * sizeof(float)); 
     std::memcpy(&u, tmp, 3 * sizeof(float)); } 
    assert(u.x == a[0]); 
    assert(u.y == a[1]); 
    assert(u.z == a[2]); 
} 

最初assertで失敗、または渡す必要のいずれか。それが失敗する表現については、その正確な表現があいまいではない方法で出現する関数を作成することは自明であるため、失敗してはならない。

ここで、tmpを省略すると、ちょっとしたことがあります。

std::memcpyは、個々のバイトの割り当てを繰り返すだけであり、明示的にスペルアウトされている可能性があります。 =演算子のセマンティクスは、簡単にコピー可能なタイプの場合、a = b;{ auto tmp = b; a = tmp; }が同等であることを意味します。 a = b; c = d;および{ auto tmp1 = b; auto tmp2 = d; a = tmp1; c = tmp2; }などと同じです。前者は直接memcpyのものですが、後者は2つでmemcpyからtmpまでです。

一方、charの配列のコピーおよびコピーアウトの許可は、機能的に同等ではなく、実際の配列charを必要とするものとして読み取ることができます。

個人的には、私が実際にその解釈を使用する実装を見つけない限り、おそらく心配しないでしょうが、安全にプレイしたい場合は、そのような一時的な配列を導入して、それは離れている。

-3

同じ型の3つのメンバーの構造体が同じ型の配列と同等であるということは、基本的にメモリの位置合わせが原因であるとは限りません。

https://en.wikipedia.org/wiki/Data_structure_alignment

あなたのコードは、C++コンパイラで大丈夫実行し、別の上に、さらに別の構成で同じコンパイラに失敗する可能性があります。

また、誤って配列ポインタを使用していることにも注意してください。

std::memcpy(a, &v, 3 * sizeof(float)); 

なく

std::memcpy(&a, &v, 3 * sizeof(float)); 

すでに限り、あなたの構造型は任意のパディングを持っていないよう

+1

'float'は、どこに格納されているかにかかわらず常に同じアラインメント要件を持つため、アラインメントの問題はありません。しかし、パディングは心配していますが、私は 'static_assert'を使ってパディングを防ぎます。配列へのポインタ配列対第1配列要素配列へのポインタに関しては、ポインタからボイドに変換されたときに同じであるので重要ではありません。 –

+0

@yurikilochek他のタイプは、それらが構造体(x86 GNU/Linuxシステムでは 'long long')に含まれているかどうかによって異なる位置合わせ要件があります。なぜ浮動できないのですか?ただし、パディングチェックが機能していれば、ここで問題は発生しません。 – hvd

+0

@hvd私はすべてのタイプのために真実であった印象を受けていました。しかし、あなたは正しいです。パディングが導入されていないかどうかは関係ありません。 –

関連する問題