2012-12-04 92 views
2

我在C中遇到嚴格的別名問題。我正在使用GCC 4.7.1。

實施例1:
當編譯該代碼與-fstrict走樣-Wstrict混疊= 3我得到 「警告:提領類型-punned指針將打破嚴格走樣規則」嚴格的鋸齒規則,假陽性還是假陰性?

#include <stdio.h> 
#include <stdint.h> 

int main(void) 
{ 
    uint8_t a[4] = {0x01, 0x23, 0x45, 0x67}; 
    uint32_t b; 

    b = *(uint32_t *)a; 

    printf("%x\n", b); 

    return(0); 
} 


實施例2:
此代碼給出了-fstrict混疊和-Wstrict走樣= 3或-Wstrict走樣= 2或-Wstrict走樣沒有警告= 1

#include <stdio.h> 
#include <stdint.h> 

int main(void) 
{ 
    uint8_t a[4] = {0x01, 0x23, 0x45, 0x67}; 
    uint32_t b; 
    void *p; 

    p = a; 
    b = *(uint32_t *)p; 

    printf("%x\n", b); 

    return(0); 
} 


這兩個示例都能正常工作。

使用聯合也是未定義的行爲,在我的情況下使用memcpy()速度太慢。
因此,第一個例子是安全的(假陽性)還是第二個例子也是不安全的(假陰性)或...?

謝謝。

+2

C99 TC3腳註82明確允許使用聯合進行類型雙擊。 –

+0

很高興知道。謝謝! – LightBit

+4

別名不是你的問題,對齊是。沒有一個'uint8_t' aka'unsigned char'數組是對齊的,這樣你就可以通過指向'uint32_t'的指針讀取它。不要那樣做,你就會陷入困境。 (另一種方法是好的。) –

回答

4

如果您想從4 uint8_t製造uint32_t,那就做:製造它。不要試圖通過指針轉換某件事物。您提交的代碼會因結果的不同而有所不同,具體取決於您的平臺是小序列還是大序列,更不用說它只是錯誤的。

他們是不好。無論如何,提供的兩個樣本都是不安全的。數據對齊要求被這樣的轉換忽略。如果你正在投射的任何東西需要比你投射的任何東西更具限制性的陣容,那麼你就會引發總線錯誤。注意最初的警告。中間無效指針簡單地掩蓋了問題(就像它對大多數問題所做的那樣)。

你想知道當你建立那個uin32_t什麼字節是「哪裏」。

uint8_t a[4] = {0x01, 0x23, 0x45, 0x67}; 
uint32_t b = ((uint32_t)a[0] << 24) | 
      ((uint32_t)a[1] << 16) | 
      ((uint32_t)a[2] << 8) | 
      (uint32_t)a[3]; 

這將總是把一個[0]字節中的目標32位的高字節無符號,一個[1]中的下一個字節,等等,無論endian'ness的。 b總是0x

+0

我只會在endian是「正確的」時才使用它,但我很擔心嚴格的別名。 – LightBit

+1

不,這不是製造'uint32_t'的方式,它是未定義的行爲,因爲對齊問題。將其轉換爲可能具有更嚴格對齊的類型絕不是一個好主意。製造'uint32_t'的方法是相反的,創建'uint32_t'並通過'unsigned char'訪問它的字節。 –

+2

@JensGustedt凡* this *引用我會同意,但演員推廣不是通過引用完成的;它是通過價值完成的。但是,我總是樂於看到另一面,所以如果你能舉出一個特殊的例子,像上面這樣的價值提升可以(或者甚至更好,*做*)引起對齊問題,我非常感興趣。同樣,如果上面有某些特別違反標準(也就是UB)的內容,請注意它違反了標準的哪一部分。我已經介紹了C99 6.3,並且沒有發現任何違規行爲,所以如果有其他事情請分享。 – WhozCraig

2

我想說的第二個例子也是不安全的 - 它只是在那種情況下,編譯器不夠聰明,發現pa實際上指向相同(1字節對齊)的位置, void *無法對齊(根據定義 - 什麼是sizeof(void)?),它不會發出警告。

+0

不,我認爲鑄造到'void *'告訴編譯器不要期待有關該指針的任何事情,所以從一個別名的POV這是沒問題的。更大的問題是對齊。這就是在這裏未定義行爲的原因。 –

+0

@JensGustedt是的,我沒有告訴其他任何事情(至少我希望這很清楚我的意思)。 – 2012-12-04 19:31:59

+0

對我來說不是很清楚。事實上,編譯器*必須足夠聰明才能看出他不能假設任何關於'* p'的事情。 –

1

在這兩種情況下,您正在使用另一種類型(uint32_t)訪問數組元素(類型爲uint8_t),該類型不是原始類型的已簽名變體,也不是字符類型。

C表示您必須通過自己的類型或簽名變體或字符類型來訪問對象,否則您違反了別名規則。