最近調試網絡的服務端程式,自己寫了一個小用戶端程式來測試,發現服務程式解包錯誤。經調試發現用戶端的協定頭大小和伺服器端的協定頭大小不一緻。原因是伺服器端加了#pragma pack(1),而用戶端沒加。
之前沒接觸過這個編譯宏,現在來認真學習之。
首先google之~~
原來#pragma pack有幾種形式,我所接觸到的是#pragma pack(n),即變量以n位元組對齊。
變量對齊在每個系統中是不一樣的,預設的對齊方式能有效的提高cpu取指取數的速度,但是可能會浪費一定的空間。在網絡程式中采用#pragma pack(1),即變量緊縮,不但可以減少網絡流量,還可以相容各種系統,不會因為系統對齊方式不同而導緻解包錯誤。
了解了概念和優點,現在我們就來測試之~
平台:cpu—pentium e5700 記憶體—2g
1.作業系統:ubuntu 11.04 32bit 編譯器:g++ 4.5.2
2.作業系統:windows xp 編譯器:vs2010
先看第一個測試。
結構體在正常情況和緊縮情況在以上不同環境下占用的記憶體大小。
1 struct pack {
2 int i;
3 short s;
4 double d;
5 char c;
6 short f;
7 }
測試結果為:
1:

2:
測試結果分析:
可以看出緊縮後結構體的大小為15,是結構體内置類型大小的和。但是在預設情況下,結構體的大小都是對齊位元組數的倍數。ubuntu下pack隻需要20個位元組,而windows要24個位元組。這是因為ubuntu是以4位元組對齊,而windows則是以最大的内置類型的位元組數對齊,在結構體内最大的内置類型為double,其大小為8個位元組。他們在記憶體中的對齊方式如下圖:
還需注意的是,在對齊類型的内部都是以2位元組對齊的。
結論:在預設情況下,linux作業系統是以4位元組對齊,windows作業系統則是以最大的内置類型對齊。
第二個測試
一個結構體内包含另外一個結構體,其大小的情況。
内部的結構體為
2 short s;
3 double d;
4 }
外部的結構體為
1 struct complex _pack{
2 char c;
3 struct pack s;
5 };
我們有四種情況:
1. pack緊縮,complex _pack緊縮
2. pack緊縮,complex _pack預設
3. pack預設,complex _pack緊縮
4. pack預設,complex _pack預設
以下的排列均按此順序。
測試的結果
1:
測試結果分析:
在兩個作業系統下,除了第一種情況----内結構體和外結構體都緊縮----相同之外,其他三種情況都不相同。我們可以根據偏移畫出結構體在記憶體中的情況。第一種情況省略。
1:
2:
結論:#pragma pack隻影響目前結構體的變量的對齊情況,并不會影響結構體内部的結構體變量的排列情況。或者說#pragma pack的作用域隻是一層。我們由第三種情況,内部結構體正常,外部結構體緊縮,可以得出結構體的對齊是按偏移計算的。
這裡還有一個問題沒解決,為什麼第二種情況内部結構體的偏移都是1?不是4或者8?