2011-06-05 94 views
10

想象一下由32位,16位和8位成員值組成的結構。成員值的順序是每個成員都在自然邊界上。結構成員對齊 - 是否可以假設沒有填充

struct Foo 
{ 
    uint32_t a; 
    uint16_t b; 
    uint8_t c; 
    uint8_t d; 
    uint32_t e; 
}; 

對於Visual C++,成員對齊和填充規則爲documented。 sizeof(Foo)在VC++上,上面的結構可預測爲「12」。

現在,我很確定規則是不應該對填充和對齊做出假設,但實際上,其他操作系統上的其他編譯器是否也會做出類似的保證?

如果不是,GCC上是否有等價的「#pragma pack(1)」?

+0

在C中,'sizeof(Foo)'中的'Foo'沒有聲明。該表達式是一個錯誤(除非'Foo'被定義/在其他地方聲明)。我建議你不要嘗試編寫多語言源文件:缺點比你可能找到的任何專業版都要大得多。 – pmg 2011-06-05 09:28:51

+0

@pmg:Pedantry。 :)'typedef struct Foo {...} Foo;' – Xeo 2011-06-05 09:33:31

+0

我相信'typedef'在C++中是多餘的(因此是一個安全可移除的bug源)。我的觀點仍然是:編寫多語言源文件比它的價值更麻煩。 – pmg 2011-06-05 09:41:59

回答

6

在實際提供這些類型的系統上,它很可能工作。比方說,一個36位系統,這些類型首先不可用。

GCC提供了一個屬性

__attribute__ ((packed))

隨着類似的效果。

+0

它不是依賴於類型的,而是依賴於架構。例如,某些16位或32位處理器無法直接尋址8位地址,因此,如果沒有填充,可能不得不生成額外的移位和屏蔽指令,以便從16位或32位存儲器讀取中提取8位值。這種情況比假設的36位系統(例如ARM7)更常見。 – Clifford 2011-06-05 10:16:34

+0

@Clifford:這不常見。如果編譯器存在的話,編譯器已經必須生成8位類型的代碼(因爲它不能在數組中對齊),所以它沒有額外的限制。一般來說,類型的「對齊」決不能大於它的大小(並且必須劃分它的大小),因此編譯器在結構中使用類型時強加更大的對齊將是病態的。 – 2011-06-05 11:16:52

+0

我並不是說編譯器在必要時無法或不會爲8位訪問生成代碼,而是出於性能方面的考慮,它會避免使用結構。一個數組保證是連續的,一個結構不是。我會建議ARM7已經足夠普及了,但是因爲這是Linux的標籤,所以在這種情況下並不多。 – Clifford 2011-06-06 19:46:31

7

總的來說,你是正確的,這不是一個安全的假設,雖然你會經常得到你期望在許多系統上的包裝。當你使用gcc時,你可能想在你的類型上使用packed屬性。

E.g.

struct __attribute__((packed)) Blah { /* ... */ }; 
+0

這是相當危險的,完全沒有必要。看到我的答案。 – 2011-06-05 14:06:04

+0

@R:我完全同意。 – 2011-06-05 14:10:35

5

在實踐中,如果uintXX_t類型存在任何系統上,你會得到不填充所需的對齊方式。不要扔在醜陋的海灣合作委員會,以保證它。

編輯:爲了詳細說明爲什麼它可能是有害的使用attribute packedaligned,它可能會導致整個結構作爲更大結構的成員或在堆棧上使用時爲錯位。這肯定會損害性能,並且在非x86機器上會產生更大的代碼。這也意味着它是無效採取指向結構的任何成員的指針,因爲通過指針訪問值的代碼將不會意識到它可能會錯位,因此可能會出錯。

至於爲什麼沒有必要,請記住attribute是特定於gcc和gcc-workalike編譯器的。 C標準不會使對齊不明確或未指定。它是實現定義的這意味着執行是需要進一步指定和文件它的行爲。 gcc的行爲始終是,將每個結構成員與其自然對齊的下一個邊界對齊(在結構之外使用時,它將具有相同的對齊方式,這必然是一個平均分割類型大小的數字) 。由於attribute是一個gcc特性,如果你使用它,你已經假設一個類似於gcc的編譯器,但是假設你已經有了你想要的對齊方式。