關於BCB Alignment 觀念 |
答題得分者是:aftcast
|
ddy
站務副站長 發表:262 回覆:2105 積分:1169 註冊:2002-07-13 發送簡訊給我 |
小弟有一個問題苦思好久,希望提出來與各位大大討論
關於BCB 在struct 對齊的問題 程式如下: [code cpp] AnsiString s,t; #pragma pack(push,1) struct system_conf { char name[16]; char id; char mode; }; struct aa{ char a; int b; char c; } ; #pragma pack(pop) t.sprintf("pack(8): struct system_conf size=%d,struct aa size=%d",sizeof(struct system_conf),sizeof(struct aa)); Memo1->Lines->Add(t); struct system_conf sys; struct aa test; sprintf(sys.name,"%s","ABCDEFGHIJKLMNO"); sys.id = 0x99; sys.mode = 0xaa; test.a ='a'; test.b =0x12345678; test.c ='c'; t.sprintf("struct system_conf:"); Memo1->Lines->Add(t); t=""; for(int i=0;i char *p = (char*) &sys; t = s.sprintf("x ",(unsigned char)*(p i)); } Memo1->Lines->Add(t); s="";t=""; t =s.sprintf("struct aa:"); Memo1->Lines->Add(t); for(int i=0;i char *p = (char*) &test; t = s.sprintf("x ",(unsigned char)*(p i)); } Memo1->Lines->Add(t); [/code] 我將#pragma pack(push,n) n 設為 1,2,4,8 每次執行記錄其結果如下: pack(1): struct system_conf size=18,struct aa size=6 struct system_conf: 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 99 aa struct aa: 61 78 56 34 12 63 pack(2): struct system_conf size=18,struct aa size=8 struct system_conf: 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 99 aa struct aa: 61 88 78 56 34 12 63 00 pack(4): struct system_conf size=18,struct aa size=12 struct system_conf: 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 99 aa struct aa: 61 00 00 00 78 56 34 12 63 f5 12 00 pack(8): struct system_conf size=18,struct aa size=12 struct system_conf: 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 99 aa struct aa: 61 00 00 00 78 56 34 12 63 f5 12 00 (紅色是編譯器填充的資料) 問題.... 1. struct system_conf 的size 為何不會隨 #pragma pack 的設定而變化… 是否是因為 sys.id 與sys.mode 為 char ,鄰接於char[]之後無法識別? 在小弟的認知裡,struct system_conf若為 pack(4) 其長度應為 20 其內容應為: 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 99 aa 00 00 難道是我的觀念有誤? 2. struct aa 在 #pragma pack(4) 與(8) 時結果一樣,是否是因為在32位元系統裡(8)與(4)一樣? 附帶一提,好玩是的在gcc 下結果還不一樣... 我把程式改寫後以MinGW 的gcc 來編譯執行 [code cpp] struct system_conf { char name[16]; char id; char mode; } __attribute__ ((aligned (4))); [/code] 其長度竟然是 20 資料為:41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 99 aa 00 00 應該是正確的 __attribute__ ((aligned (4))) 和 #pragma pack(4) 的結果還不同… 到底是那裡的問題呢?? |
aftcast
站務副站長 發表:81 回覆:1485 積分:1763 註冊:2002-11-21 發送簡訊給我 |
題目出的真好~! 難得有這麼高水準的問題 : )
我把自己研究已久的感言發表一下囉,若有錯誤,尚請指教! 前言: 1/ 所謂的alignment指的是對齊記憶體的位址線,會這樣做的原因和cpu暫存器很有關係,比如16位元時(286時代)通常為了加速而對齊2byte,即偶數位址,因為暫存器是16位元的。 2/ compiler額外填入的值叫padding,又可分為internal padding和trailing padding。internal是指成員間所加入。trailing是最後一成員後面額外加入的,這個觀念很重要,也是本題解答的關鍵。 規則: (部份是個人研究的結果,不敢完全保證正確) 1/ 若成員型別的size大於等於指定的alignment,那就要對齊alignment倍數的位址上。 2/ 若成員型別的size小於alignment,也要考慮次alignment(個人研究心得) 3/ 成員若是一陣列型別,那要考慮的是陣列所指向的型別,而非本身陣列的大小。這點用說的可能不好理解。舉例來說,short[16]這個成員,你要判別的不是32bytes是否有大於等於或小於alignment,而是short(2Bytes)這個型別的大小是否大於等於或小於alignment。 重要說明: 針對規則2的部份: 以修改ddy下面的例子來說,若 struct aa{ char a; //int b; short b; char c; } ; 以4bytes來做aligment,struct的內容為 61 d4 78 56 63 00 (size=6,並非8) <<---對最後只補一個00更疑惑? 先別急著想,最後有答案… 為何會加入d4這一個byte? 因為short(2byte)比指定的alignment4小,但它還是要對齊自己的2byte(對暫存器來說,有加速到),即偶數位址,即我所謂的次對齊。從此觀點可以解答為何pack8與pack4的答案會是一樣的。因為例子中所有的型別都小於8bytes,所以就各自align (次對齊)。 可以把ddy的例子改成如下觀查之: struct aa{ char a; __int64 b; char c; } ; 這時候pack8就會看出來有不同。因為__int64等於8bytes。alignment就產生了主變化,即一般所了解的情形。 61 50 fe 73 01 00 00 00 78 56 34 12 00 00 00 00 63 00 00 00 01 00 00 00 針對trailing padding的進一步解說: (解決為何ddy例子中所有的system_conf 的size都一樣) 一般來說,成員間的padding大家都可以理解,但最後一個成員的後面到底要不要padding?? 有想過嗎? 個人研究的結果,trailing padding要考慮的是當自定型別struct若為應用在陣列上,會發生什麼事?! 比如說 system_conf foo[10]; 為了能正確的指出每個陣列的元素,那麼元素的大小就要正確。在此,元素是一個自定的struct型別。想像一下,有trailing padding與否,會有什麼差別? 以本例來說,因為struct裡通通都是char型別(請額外對照一下規則3),所以沒有trailing padding是ok的,一切都不會出問題。(再往下看可能更了解) 若把system_conf修改為 struct system_conf { char name[16]; int id; char mode; }; //然後alignment4 內容為:41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 99 00 00 00 aa f5 12 00 (size=24) 為了讓int id 能夠在下個struct元素時(比如說foo[2])有對齊到4的倍數位址,trailing padding是必要的!!! 但若所有的型別都是char,即1個byte,那整個陣列從1至10,其實全都有對齊到,或者說全都無需對齊。這就是為何system_conf 的size都一樣! 最後,進一步的,請思考最上面的問題 : 61 d4 78 56 63 00 <<---為何只補一個byte(00) 明明是alignment4,你期待的提補3個byte是吧? 因為short是2byte,它只要對齊偶數位址就可以,故僅把trailing padding加入1個byte就足夠了! 想想,是這樣沒錯吧!? : ) 至於,若是加入你要的3個bytes,那是否也可以呢? 當然也是正確的,只是浪費一些空間而已。這或許也說明了 MinGW 的gcc 的結果是符合ddy的預期,且gcc編的程式也不會出錯!! borland compiler 似乎會精簡記憶體的浪費,但依舊保持程式的正確性! (個人心得) 希望這篇文章對想深入了解高深一點的技術的同好有些幫助~! Good Luck
------
蕭沖 --All ideas are worthless unless implemented-- C++ Builder Delphi Taiwan G+ 社群 http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2009-07-27 12:24:49, 註解 無‧
|
ddy
站務副站長 發表:262 回覆:2105 積分:1169 註冊:2002-07-13 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |