簡體   English   中英

再一次:嚴格的別名規則和char *

[英]Once again: strict aliasing rule and char*

我讀的越多,我就越困惑。

相關問題的最后一個問題與我的問題最接近,但是我對所有關於對象生命周期的問題感到困惑,尤其是 - 只讀或不讀。


直截了當。 如我錯了請糾正我。

這很好,gcc沒有發出警告,我正試圖“通過char* ”讀取類型Tuint32_t ):

uint32_t num = 0x01020304;
char* buff = reinterpret_cast< char* >( &num );

但這是“壞”(也是一個警告),我正在嘗試“反過來”:

char buff[ 4 ] = { 0x1, 0x2, 0x3, 0x4 };
uint32_t num = *reinterpret_cast< uint32_t* >( buff );

第二個如何與第一個不同,特別是當我們談論重新排序指令(用於優化)時? 另外,添加const不會以任何方式改變這種情況。

或者這只是一條直接規則,它明確指出:“這可以在一個方向完成,但在另一個方向不能完成”? 我在標准中找不到任何相關內容(特別是在C ++ 11標准中搜索過)。

C和C ++是否相同(因為我讀了一條評論,暗示它與2種語言不同)?


我用union來“變通”這一點,這似乎仍然是不是 100%確定,因為它不是由標准的保證(其中規定,我只能靠價值,這是在最后一次修改union )。

所以,經過大量閱讀,我現在更加困惑。 我想只有memcpy才是“好”的解決方案?


相關問題:


編輯
現實世界的情況:我有一個第三方庫( http://www.fastcrypto.org/ ),它計算UMAC,返回值在char[ 4 ] 然后我需要將其轉換為uint32_t 而且,順便說一下,lib使用的東西很像((UINT32 *)pc->nonce)[0] = ((UINT32 *)nonce)[0] 無論如何。

另外,我問的是什么是對的,什么是錯的以及為什么。 不僅關於重新排序,優化等(有趣的是, -O0沒有警告,只有-O2 )。

請注意 :我知道大/小端情況。 情況並非如此。 我真的想忽略這里的字節序。 “嚴格的別名規則”聽起來像是非常嚴肅的事情,遠比錯誤的字節序嚴重得多。 我的意思是 - 就像訪問/修改內存一樣,不應該被觸及; 任何一種UB都可以。

標准 (C和C ++)的引用將非常感激。 我找不到任何關於別名規則或任何相關的內容。

第二個如何與第一個不同,特別是當我們談論重新排序指令(用於優化)時?

問題在於編譯器使用規則來確定是否允許這樣的優化。 在第二種情況下,您嘗試通過不兼容的指針類型讀取char[]對象,這是未定義的行為; 因此,編譯器可能會重新排序讀取和寫入(或執行您可能不期望的任何其他操作)。

盡管看起來不自然,但你真的不得不考慮你認為編譯器可能如何優化,並且只是遵守規則

或者這只是一條直接規則,它明確指出:“這可以在一個方向完成,但在另一個方向不能完成”? 我在標准中找不到任何相關內容(特別是在C ++ 11標准中搜索過)。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf第3.10章第10段。

在C99中,我認為也是C11,它是6.5第7段。

C和C ++都允許通過char * (或者特別是char類型的左值)訪問任何對象類型。 它們不允許通過任意類型訪問char對象。 所以是的,規則是一種“單向”規則。

我使用union來“解決”這個問題,這似乎仍然不是100%正常,因為標准並不保證(這表明我只能依賴於最后修改的值)。

盡管該標准的措辭非常模糊,但在C99(及以后)中,很明顯(至少從C99 TC3開始) 意圖是允許通過聯合進行類型懲罰。 但是,您必須通過聯合執行所有訪問(特別是您不能僅僅為了類型懲罰而'將聯合轉換為存在')。

返回的值在char [4]中。 然后我需要將其轉換為uint32_t

只需使用memcpy或手動將字節移動到正確的位置,以防字節排序成為問題。 好的編譯器無論如何都可以優化它(是的,甚至是對memcpy的調用)。

我使用union來“解決”這個問題,這似乎仍然不是100%正常,因為標准並不保證(這表明我只能依賴於最后修改的值)。

Endianess就是這個原因。 具體地,字節序列01 00 00 00可以表示1或16,777,216。

執行您正在執行的操作的正確方法是停止嘗試欺騙編譯器為您執行轉換並自行執行轉換。

例如,如果char[4]是little-endian(最小字節優先),那么你會做類似下面的事情。

char[] buff = new char[4];
uint32_t result = 0;
for (int i = 0; i < 4; i++)
    result = (result << 8) + buff[i];

這會手動執行兩者之間的轉換,並保證在進行數學轉換時始終正確。

現在,如果您正在快速進行此轉換,那么使用#if和您的架構知識來使用枚舉來自動執行此操作可能是有意義的,如您所提到的,但這再次遠離便攜式解決方案。 (如果你不能確定,你可以使用這樣的東西作為你的后備)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM