0

Here已被確定爲將T類型的緊密壓縮的連續結構成員視爲T的數組是非法的。std :: memcpy結構與緊密壓縮的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

只要你的結構類型沒有任何填充,有標準它沒有明確支持,但可以推斷出對它非常接近的東西的支持。

給出一個平凡複製的類型T,有什麼明確允許是其代表性複製到的char(或unsigned char)和背部的數組。

沒有要求將數組內容保存在數組本身中。內容可以存儲在一個文件中,並在隨後執行該程序時重新讀取。或者存儲在不同類型的對象中,只要該類型允許。爲此,實現必須允許表示對象,當這些表示不是源自同一運行中的T類型的對象時。

其結果是,在最起碼,

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呢,後者是什麼兩個memcpy S通過tmp做。

另一方面,允許複製進出數組char可以被理解爲需要實際的數組char,而不僅僅是它的功能等同物。我個人可能不會擔心,除非我真的遇到了一個使用這種解釋的實現,但是如果你想安全地使用它,你可以引入這樣一個臨時數組,並且驗證你的編譯器能夠優化它走了。

-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'來防範這些問題。至於指針到數組和指針到數組的第一個元素,沒關係,因爲它們在轉換爲void指針時是相同的。 –

+0

@yurikilochek其他類型有不同的對齊要求,具體取決於它們是否是struct(x86 GNU/Linux系統中的「long long」)的一部分,爲什麼不能'浮動'?但是如果您的填充檢查正在工作,則對齊不應該導致問題。 – hvd

+0

@ hvd我的印象是所有類型都是真實的,而不僅僅是花車。但是你是對的,如果沒有引入填充就沒關係。 –

相關問題