作者EdisonX (閉上眼的魚)
看板C_and_CPP
標題Re: [問題] union + struct + bitfield的問題
時間Mon Jun 18 01:48:19 2012
拋磚引玉。
事先丟原 po 水球過, 大概知道想知道、想要的是什麼,
< 至少應該不會再被說丟了一堆他知道的東西搪塞、落跑才對 >
做個整理一下。
再次強調的是,padding 問題是真的 implement by compiler,
我只能說這些規則目前在大多 compiler 下是這麼做的。
※ 引述《rifiz (薩哈拉雅)》之銘言:
延續 case 28 之 struct
union A
{
struct
{
unsinged int p1:4;
unsigned int p2:2;
unsigned int p3:4;
unsigned int p4:6;
}
unsigned int kk;
}
union A obj = {3,2,4,5};
首先這是第一個 undefined ,目前實際上我也沒看過別人這麼做,
對 union 事實上沒辦法進行初始予以賦值。所以只能這麼做。
union A obj ;
obj.p1=3 , obj.p2=2 , obj.p3=4 , obj.p4=5;
忘了從哪版開始可這麼做
union A obj = {.p1=3 , .p2=2 , .p3=4 , .p4=5};
再來是 struct padding 一些整理,有誤更正。
1. 成員均屬同型資料型態(depends on compiler)
成員均屬同型資料型態時大多 compiler 做法是不去 padding
struct m1{ char c1, c2, c3;}
sizeof(struct m1) = 3
2. 成員不同資料型態時 (depends on compiler)
struct m1{ struct m2{ struct m3{
char a; char a; int a;
int b; char b; char b;
char c; int c; char c;
}; }; };
m1 : (a,b) offset 3bytes ,
(b,c) offset 0byte ,
(c,end) offset 3bytes ,
total = 1 + 3 + 4 + 0 + 1 + 3 = 12
m2 : (a,b) offset 0byte (同資料態型不 padding),
(b,c) offset 2bytes (2 對齊 4 )
(c,end) offset 0byte
total = 1 + 1 + 2 + 4 = 8
m3 : (a,b) offset 0byte
(b,c) offset 0byte
(c,end) offset 2byte (2 對齊 4 )
total = 4 + 0 + 1 + 0 + 1 + 2 = 8
< 所以流傳讓 struct 變小就是將同質資料型態寫在一起,
並以大到小或小到大方式進行宣告。>
強調了,所有的 padding 還是依 compiler 為主。
像下面這個我擠破頭也不知道為什麼。
struct Student{
int ID; // 4
char name[200]; // 200
float score; // 4
double l; // 8
char *p; // 4
};
上述理應不會 padding, 結果應該是 220,
但莫名的被 compiler 後, padding 後結果變成 224, 而且 offset 在 char *p 之後。
compiler 它認為這麼做是對於速度最快的,我是算不出來為什麼,
因整個結構體也被 padding 到 4 的倍數了,
不過 vc 裡下了這個指令後一切就正常了。
#pragma pack(push,4)
struct s{
int ID; // 4
char name[200]; // 200
float score; // 4
double l; // 8
char *p; // 4
};
#pragma pack(pop)
gcc 也有相對應的指令, __attribution__((packet)) 之類的,
它讓 coder 對於 padding 上的猜測會更加準確。
3. bit-field padding
bit-field 也是 depends on compiler 嚴重的一項問題。
struct s {
unsigned a:1;
unsigned b:2;
}; // size = 4 , 其它 28 bits 不用
struct s1{
unsigned a:1;
int b:1;
}; // size = 4 , a 與 b 資料型態長度相同,用在同一 byte 裡
來講一個 ieee754 較常犯錯的例子。
struct _754 {
unsigned int mantissa : 23;
unsigned char exponment : 8 ;
unsigned char sign : 1 ;
};
看起來是 32 bits, 實際上被 padding 成 64 bits
原因在於 unsigned int 到 unsigned char 時,資料型態大小不同,
所以 exponment / mantissa 不會被緊接著放,修正方式是將
unsigned char 全改成 unsigned int 即可。
關於 bit_field 個人習慣是全用同一種、最長的資料型態,
如果真的有必要跳的話也可以自己搞, padding 不可預測情況也較低,
也少用
unsigned int : 0
這種東西,寧可自己事先算好。
-------
我一定要強調,在標準底下,上面這些 「甘哪塞」,
它只是大多 compiler < 其實也才驗證過兩個而已 > 這麼搞,
若真有如 VictorTom 之需求,
我想用 compiler 特定之 #pragma pack(push,1) 較佳,
當然這也是 speed - space trade-off 問題。
最後若要查某個變數是否 padding 、 padding 幾 bytes ,
參考這篇
http://ppt.cc/HEX- 裡面的 section 3-10
唯 bit-field 之成員由於無法使用取址運算子(&),故沒辦法查。
-------
拋磚引玉而已,參考一下,有誤或有其他心得版友也不吝提出,感激不盡 :)
--
「自從我學了 C# , 人都變聰明 , 考試都考一百分」
「自從我學了 VB , 皮膚都變好 , 人也變漂亮了 」
「自從我學了 Java , 明顯變壯 , 個子也變高了 」
「自從我學了 C++ , 內分泌失調 , 頭都禿了... 」
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 180.177.76.161
推 x000032001: 06/18 01:51
推 hilorrk:union用initializer list時會初始化the first named 06/18 02:41
→ hilorrk:member...(ISO 9899 6.7.8) 應該不是全都無法初始化吧? 06/18 02:42
→ hilorrk:至於 designated initializer 則是C99的標準 06/18 02:44
→ EdisonX:疑!! 那該說是手邊 compiler 不支援 init. list 嗎 ? 06/18 02:51
→ EdisonX:< 我和原po改用逐一assigned,觀查都屬正常 > 06/18 02:53
→ EdisonX:感謝 h 大補充 :) 06/18 02:53
推 VictorTom:220/224,應該是為了讓double那個也確保能align到:) 06/18 09:12
→ diabloevagto:在padding檢查實際offset多少,是看參數的addr嗎? 06/18 10:34
推 littleshan:C99 提供 offsetof() 06/18 13:12
→ EdisonX:嗯,c99提供offsetof,我於連結中提的paddingof也是類似原理 06/18 15:13
→ EdisonX:是將struct addr head先定到NULL,其它的再依序相減。 06/18 15:15
→ EdisonX:< 補上,謝謝 VictorTom 大的見解 :) > 06/18 15:16
推 rifiz:感恩阿~~慢慢研究中 ORZ... 06/19 16:40
→ Favonia:我先提個東西好了,C11 才有 anonymous struct/union 06/21 10:26