2012-10-24 81 views
11

假設我有以下結構:是否保證擦除填充區域的結構初始化爲零?

typedef struct 
{ 
    unsigned field1 :1; 
    unsigned field2 :1; 
    unsigned field3 :1; 
} mytype; 

前3位將是可用的,但sizeof(mytype)將返回4這意味着填充29個比特。 我的問題是,這些填充比特的標準保證是零通過的聲明初始化:

mytype testfields = {0}; 

或:

mytype myfields = {1, 1, 1}; 

這樣的,它是安全的假設進行以下memcmp()那位4..29將爲零,因此不會影響比較:

if (memcmp(&myfields, &testfields, sizeof(myfields)) == 0) 
    printf("Fields have no bits set\n"); 
else 
    printf("Fields have bits set\n"); 

回答

11

是的,沒有設置爲0。實際的標準,C11,規定:

如果具有靜態或線程存儲時限的對象不明確 初始化,然後:

  • ....

  • 如果它是一個聚合,每個成員根據這些規則初始化(遞歸) ,並且任何填充被初始化爲零位;

所以這僅適用於靜態存儲的對象,在第一個視圖。但後來它說除了:

如果在一個大括號內的列表更少初始化值多於 是用於初始化的陣列元件或聚集體的成員,或更少的字符在 字符串文字已知大小比 是數組中的元素,聚合的其餘部分應該是 隱含地初始化與具有靜態存儲的持續時間的對象相同。

所以這意味着未明確初始化的子結構內的填充是零位初始化的。概括地說,結構中的一些填充保證是零位初始化的,有些填充不是。我不認爲這種混淆是故意的,我會爲此提交一份缺陷報告。

舊版本根本沒有。因此,對於大多數現有的編譯器,您必須更加小心,因爲它們尚未實現C11。但是AFAIR,clang已經這樣做了。

另外請注意,這隻適用於初始化。填充不一定會在作業中複製。

+2

+1,有趣的是我沒有意識到這種變化。 – ouah

+4

我想你應該添加C11段落的開頭*如果具有自動存儲持續時間的對象沒有被顯式初始化,它的值是不確定的。如果具有靜態或線程存儲持續時間的對象未被明確初始化,則:*這不能保證'mytype testfields = {0};'被設置爲'0'。 – ouah

+0

@ouah,其實這似乎是一個漏洞,你是對的。我不認爲它是有意的,所以我會解釋一點。好點子。 –

6

C99標準沒有指定墊定位將被設置爲零。實際上,它明確提到任何填充位的值都是未指定的,所以填充不需要在賦值中複製。

腳註51至6.2.6.1(6)(n1570):

因此,例如,結構分配不需要複製任何填充比特。

新C2011標準 - 感謝Jens Gustedt分享知識 - 規定,在沒有明確的初始化靜態或線程存儲時限的對象填充位初始化爲0。

還有任何保證分配。

+2

嗯,這相當暗示'memcmp()'將是一種不安全的方式來比較任何有填充的結構,但不是嗎? – Benj

+0

@Benj是的,'memcmp'由於填充效果不安全。儘管填充位未使用,您*合法*無法訪問它。所以,無論標準強制填充還是不填充都不重要。 –

+0

@KingsIndian:如果通過'calloc()'或其他方式接收到內存,則可以對其進行合法訪問。另一方面,我認爲幾乎任何結構上的操作都可以做任何它喜歡的任何填充。代碼可以合法地使用指向結構體的指針來複制其中的所有字節,如果它在此期間不訪問結構體的話,但唯一保證系統關於填充字節的關鍵是它們在源結構體中的值不會造成目的地問題。 – supercat

2

我的問題是,由標準保證是零通過聲明初始化這些填充比特:

填充的值未指定:

(C99,6.2.6.1p6)「當值存儲在結構或聯合類型的對象(包括成員對象中)中時,對象表示的字節對應於任何填充字節取未指定的值」

編輯:去看延斯Gustedt answer,C11現在保證填充在(罕見)某些情況下