① 怎樣設置gcc編譯出程序的struct member align
為了解決Unix自定義結構在GCC優化編譯中對齊問題,一般解決辦法是用如下宏封裝
自定義結構
#pragma pack(1)
struct my_arphdr
{
};
#pragma pack()
如果是SPARC/Solaris,還可以這樣
struct my_arphdr
{
} __attribute__ ((packed));
兩種辦法其實都可以用在Unix系統/GCC編譯器中。
D: mbuf@smth
關於結構中位元組對齊問題,相應編譯器選項為
GCC/G++ : -fpack-struct
Sun Workshop cc/CC: -misalign
最好不這樣做,會大大降低程序效率,特別在某些架構中。應該嘗試用位操作來處理。
D: Unknown@smth
GCC可以這么解決
#ifdef __GCC__
#define PACKED __attribute__((__packed__))
#else
#define PACKED
#endif
struct msg
{
u_int16_t PACKED first;
...
};
還是 VC 簡單,#include <pshpack1.h> 就搞定了
A: gfh_nuaa
DEC : #pragma pack(1)
SUN : #pragma pack(1)
AIX : 編譯時 -q align=packed
HP-UX : #pragma pack 1
D: Joe Durusau
在 Visual C++ 中,使用 "-ZP1" 就可以讓編譯器對自定義結構進行單位元組對齊,實
際就是取消了對齊優化。
A: [email protected] 2001-12-20 13:09
1) 結構內部成員的pack
struct foo
{
char a;
int b __attribute__ ((packed));
};
2) 整個結構的pack
struct foo
{
char a;
int b;
}__attribute__ ((packed));
3) 文件范圍的pack
#pragma pack(1)
struct foo
{
char a;
int b;
};
... ...
4) 編譯選項的pack
-fpack-struct
但這是最危險的做法,因為這樣做可能會使庫函數和你的程序對結構內成員的偏移理
解不一致。
② 匯編高手來說下內存對齊背後CPU究竟是怎麼操作的
內存對齊:
我們知道現代計算機體系中CPU按照雙字、字、位元組訪問存儲內存,並通過匯流排進行傳輸,若未經一定規則的對齊,CPU的訪址操作與匯流排的傳輸操作將會異常的復雜,所以現代編譯器中都會對內存進行自動的對齊。
1.內存對齊系數
說道內存對齊,就不得不說內存對齊系數, 對齊系數最簡單的設置方法是使用 #pragma pack(n)進行設置,這部分點進鏈接在我的文章內有詳細說明!
2.sizeof
說到內存對齊第二個不得不說的就是sizeof,它的基本作用是判斷數據類型或者表達式長度,要注意的是這不是一個函數,而是一個C++中的關鍵字!位元組數的計算在程序編譯時進行,而不是在程序執行的過程中才計算出來!
3.類型的長度與數據成員對齊
你的計算機中,數據類型的長度指的就是在你的計算機中對數據類型使用sizeof得到的結果,當然這個在各種不同的編譯環境下得到的結果是不同的。
比如在32位Visual Studio環境下:
cout << sizeof(char) << endl; // 1
cout << sizeof(short) << endl; // 2
cout << sizeof(int) << endl; // 4
cout << sizeof(long) << endl; // 4
cout << sizeof(double) << endl; // 8
而在64位G++編譯環境下:
cout << sizeof(char) << endl; // 1
cout << sizeof(short) << endl; // 2
cout << sizeof(int) << endl; // 4
cout << sizeof(long) << endl; // 8
cout << sizeof(double) << endl; // 8
下面我將在32位Visual Studio環境下講解數據成員對齊:
首先我們要清楚結構體struct中的成員在內存中的分配是連續的,struct內的首地址也就是struct內第一個數據成員的地址,換句話說struct內第一個數據成員離struct開始的距離offset = 0。
數據成員對齊的規則就是,而在第一個成員之後,每個成員距離struct首地址的距離 offset, 都是struct內成員自身長度(sizeof) 與 #pragma pack(n)中的n的最小值的整數倍,如果未經對齊時不滿足這個規則,在對齊時就會在這個成員前填充空子節以使其達到數據成員對齊。
默認n為8時:
struct {
char a;
double b;
} myStruct;
cout << sizeof myStruct << endl; // 16
cout << (int *)&myStruct.a << endl; // 0024F898
cout << &myStruct.b << endl; // 0024F8A0(因運行時而異)
當設置n為4也就是min(sizeof(double), n) = 4 時:
#pragma pack(4)
struct {
char a;
double b;
} myStruct;
cout << sizeof myStruct << endl; // 12
cout << (int *)&myStruct.a << endl; // 0046F76C
cout << &myStruct.b << endl; // 0046F770
第一個例子時,最小值為8,填充7個位元組到char a 之後。
第二個例子時,最小值為4,填充3個位元組到char a之後。
4.整體對齊
編譯器在進行過數據成員對齊之後,還要進行整體對齊。與數據對齊相似但不是完全相同, 如果數據對齊完成時struct的大小不是 struct內成員自身長度最大值(sizeof) 與 #pragma pack(n)中的n的最小值的整數倍。(注意這里是成員中長度最大的那個與n比較,而不是特定的一個成員。)就要在struct的最後添加空位元組直到對齊。
當設置n為4也就是min(sizeof(short), n) = 2 時:
#pragma pack(4)
struct {
char a;
short b;
char c;
} myStruct;
cout << sizeof myStruct << endl; // 6
cout << (int *)&myStruct.a << endl; // 003DFED0
cout << &myStruct.b << endl; // 003DFED2
cout << (int *)&myStruct.c << endl; // 003DFED4
在上面的例子中,char a offset為0 因成員對齊占據[D0]填充[D1]共兩個位元組,short b是最大長度成員無需對齊占據[D2-D3]兩個位元組,它的offset是2,而char c的offset是4占據[D4]無需成員對齊,但此時struct的大小是2+2+1 = 5位元組,不是2的整數倍,所以我們要填充空子節在最後直到struct大小達到2的整數倍,這就是整體對齊。
經過了數據成員對齊與整體對齊之後內存對齊就完成了,如果深入思考上述規則還會發現:即使是同樣數目與數量的數據成員,在擺放的順序不同時struct的大小也會不同,下面就是一個例子:
這樣擺放是12位元組:
卻變成了8位元組.png
由於這種特性,如果在網路編程或相關內存操作時如果不加以注意的話,就會造成隱秘而難以糾正的錯誤,請大家務必小心!
③ c++中內存是如何對齊的
有虛函數的話就有虛表,虛表保存虛函數地址,一個地址佔用的長度根據編譯器不同有可能不同,vs裡面是8個位元組,在devc++裡面是4個位元組。類和結構體的對齊方式相同,有兩條規則
1、數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset為0的地方,以後每個數據成員的對齊按照#pragma pack指定的數值和這個數據成員自身長度中,比較小的那個進行。
2、結構(或聯合)的整體對齊規則:在數據成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行
下面是我收集的關於內存對齊的一篇很好的文章:
在最近的項目中,我們涉及到了「內存對齊」技術。對於大部分程序員來說,「內存對齊」對他們來說都應該是「透明的」。「內存對齊」應該是編譯器的 「管轄范圍」。編譯器為程序中的每個「數據單元」安排在適當的位置上。但是C語言的一個特點就是太靈活,太強大,它允許你干預「內存對齊」。如果你想了解更加底層的秘密,「內存對齊」對你就不應該再透明了。
一、內存對齊的原因
大部分的參考資料都是如是說的:
1、平台原因(移植原因):不是所有的硬體平台都能訪問任意地址上的任意數據的;某些硬體平台只能在某些地址處取某些特定類型的數據,否則拋出硬體異常。
2、性能原因:數據結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在於,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。
二、對齊規則
每個特定平台上的編譯器都有自己的默認「對齊系數」(也叫對齊模數)。程序員可以通過預編譯命令#pragma pack(n),n=1,2,4,8,16來改變這一系數,其中的n就是你要指定的「對齊系數」。
規則:
1、數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset為0的地方,以後每個數據成員的對齊按照#pragma pack指定的數值和這個數據成員自身長度中,比較小的那個進行。
2、結構(或聯合)的整體對齊規則:在數據成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行。
3、結合1、2顆推斷:當#pragma pack的n值等於或超過所有數據成員長度的時候,這個n值的大小將不產生任何效果。
三、試驗
我們通過一系列例子的詳細說明來證明這個規則吧!
我試驗用的編譯器包括GCC 3.4.2和VC6.0的C編譯器,平台為Windows XP + Sp2。
我們將用典型的struct對齊來說明。首先我們定義一個struct:
#pragma pack(n) /* n = 1, 2, 4, 8, 16 */
struct test_t {
int a;
char b;
short c;
char d;
};
#pragma pack(n)
首先我們首先確認在試驗平台上的各個類型的size,經驗證兩個編譯器的輸出均為:
sizeof(char) = 1
sizeof(short) = 2
sizeof(int) = 4
我們的試驗過程如下:通過#pragma pack(n)改變「對齊系數」,然後察看sizeof(struct test_t)的值。
1、1位元組對齊(#pragma pack(1))
輸出結果:sizeof(struct test_t) = 8 [兩個編譯器輸出一致]
分析過程:
1) 成員數據對齊
#pragma pack(1)
struct test_t {
int a; /* 長度4 < 1 按1對齊;起始offset=0 0%1=0;存放位置區間[0,3] */
char b; /* 長度1 = 1 按1對齊;起始offset=4 4%1=0;存放位置區間[4] */
short c; /* 長度2 > 1 按1對齊;起始offset=5 5%1=0;存放位置區間[5,6] */
char d; /* 長度1 = 1 按1對齊;起始offset=7 7%1=0;存放位置區間[7] */
};
#pragma pack()
成員總大小=8
2) 整體對齊
整體對齊系數 = min((max(int,short,char), 1) = 1
整體大小(size)=$(成員總大小) 按 $(整體對齊系數) 圓整 = 8 /* 8%1=0 */ [注1]
2、2位元組對齊(#pragma pack(2))
輸出結果:sizeof(struct test_t) = 10 [兩個編譯器輸出一致]
分析過程:
1) 成員數據對齊
#pragma pack(2)
struct test_t {
int a; /* 長度4 > 2 按2對齊;起始offset=0 0%2=0;存放位置區間[0,3] */
char b; /* 長度1 < 2 按1對齊;起始offset=4 4%1=0;存放位置區間[4] */
short c; /* 長度2 = 2 按2對齊;起始offset=6 6%2=0;存放位置區間[6,7] */
char d; /* 長度1 < 2 按1對齊;起始offset=8 8%1=0;存放位置區間[8] */
};
#pragma pack()
成員總大小=9
2) 整體對齊
整體對齊系數 = min((max(int,short,char), 2) = 2
整體大小(size)=$(成員總大小) 按 $(整體對齊系數) 圓整 = 10 /* 10%2=0 */
3、4位元組對齊(#pragma pack(4))
輸出結果:sizeof(struct test_t) = 12 [兩個編譯器輸出一致]
分析過程:
1) 成員數據對齊
#pragma pack(4)
struct test_t {
int a; /* 長度4 = 4 按4對齊;起始offset=0 0%4=0;存放位置區間[0,3] */
char b; /* 長度1 < 4 按1對齊;起始offset=4 4%1=0;存放位置區間[4] */
short c; /* 長度2 < 4 按2對齊;起始offset=6 6%2=0;存放位置區間[6,7] */
char d; /* 長度1 < 4 按1對齊;起始offset=8 8%1=0;存放位置區間[8] */
};
#pragma pack()
成員總大小=9
2) 整體對齊
整體對齊系數 = min((max(int,short,char), 4) = 4
整體大小(size)=$(成員總大小) 按 $(整體對齊系數) 圓整 = 12 /* 12%4=0 */
4、8位元組對齊(#pragma pack(8))
輸出結果:sizeof(struct test_t) = 12 [兩個編譯器輸出一致]
分析過程:
1) 成員數據對齊
#pragma pack(8)
struct test_t {
int a; /* 長度4 < 8 按4對齊;起始offset=0 0%4=0;存放位置區間[0,3] */
char b; /* 長度1 < 8 按1對齊;起始offset=4 4%1=0;存放位置區間[4] */
short c; /* 長度2 < 8 按2對齊;起始offset=6 6%2=0;存放位置區間[6,7] */
char d; /* 長度1 < 8 按1對齊;起始offset=8 8%1=0;存放位置區間[8] */
};
#pragma pack()
成員總大小=9
2) 整體對齊
整體對齊系數 = min((max(int,short,char), 8) = 4
整體大小(size)=$(成員總大小) 按 $(整體對齊系數) 圓整 = 12 /* 12%4=0 */
5、16位元組對齊(#pragma pack(16))
輸出結果:sizeof(struct test_t) = 12 [兩個編譯器輸出一致]
分析過程:
1) 成員數據對齊
#pragma pack(16)
struct test_t {
int a; /* 長度4 < 16 按4對齊;起始offset=0 0%4=0;存放位置區間[0,3] */
char b; /* 長度1 < 16 按1對齊;起始offset=4 4%1=0;存放位置區間[4] */
short c; /* 長度2 < 16 按2對齊;起始offset=6 6%2=0;存放位置區間[6,7] */
char d; /* 長度1 < 16 按1對齊;起始offset=8 8%1=0;存放位置區間[8] */
};
#pragma pack()
成員總大小=9
2) 整體對齊
整體對齊系數 = min((max(int,short,char), 16) = 4
整體大小(size)=$(成員總大小) 按 $(整體對齊系數) 圓整 = 12 /* 12%4=0 */
四、結論
8位元組和16位元組對齊試驗證明了「規則」的第3點:「當#pragma pack的n值等於或超過所有數據成員長度的時候,這個n值的大小將不產生任何效果」。另外內存對齊是個很復雜的東西,上面所說的在有些時候也可能不正確。呵呵^_^
[注1]
什麼是「圓整」?
舉例說明:如上面的8位元組對齊中的「整體對齊」,整體大小=9 按 4 圓整 = 12
圓整的過程:從9開始每次加一,看是否能被4整除,這里9,10,11均不能被4整除,到12時可以,則圓整結束。
④ DEV C++ 中怎麼把C\C++代碼對齊
選中多行,按組合鍵「shift+tab」,是整體前移一個tab的位置,按「tab」則是向後移動一個tab的位置。
通過這種方式可以實現代碼的快速對齊。
Bloodshed Dev-C++是一個Windows下的C和C++程序的集成開發環境。Bloodshed Dev-C++使用MingW32/GCC編譯器,遵循C/C++標准。開發環境包括多頁面窗口、工程編輯器以及調試器等,在工程編輯器中集合了編輯器、編譯器、連接程序和執行程序,提供高亮度語法顯示的,以減少編輯錯誤,還有完善的調試功能,