博客轉載自:http://blog.csdn.net/lime1991/article/details/44536343
1.什么是對齊?為什么要對齊?
2.pragma pack語法
作用:指定結構,聯合和類的包對齊方式(pack alignment)
show(optical):顯示當前packing aligment的字節數,以warning message的形式顯示。
push(optical):
Pushes the current packing alignment value on the internal compiler stack, and sets the current packing alignment value to n. If n is not specified, the current packing alignment value is pushed.
pop(optical):
Removes the record from the top of the internal compiler stack. If n is not specified with pop, then the packing value associated with the resulting record on the top of the stack is the new packing alignment value. If n is specified, for example, #pragma pack(pop, 16), n becomes the new packing alignment value. If you pop with identifier, for example, #pragma pack(pop, r1), then all records on the stack are popped until the record that hasidentifier is found. That record is popped and the packing value associated with the resulting record on the top of is the stack the new packing alignment value. If you pop with an identifier that is not found in any record on the stack, then the pop is ignored.
identifier(optional):
When used with push, assigns a name to the record on the internal compiler stack. When used with pop, pops records off the internal stack until identifieris removed; if identifier is not found on the internal stack, nothing is popped.
n (optional):
Specifies the value, in bytes, to be used for packing. If the compiler option /Zp is not set for the module, the default value for n is 8. Valid values are 1, 2, 4, 8, and 16. The alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.
3.結構體對齊規則
2)將各數據成員內存對齊,按各自對齊模數而填充的字節數累加到和sum_a上,記為sum_b。對齊模數是【該數據成員所占內存】與【#pragma pack指定的數值】中的較小者。
3)將和sum_b向結構體模數對齊,該模數是【#pragma pack指定的數值】、【未指定#pragma pack時,系統默認的對齊模數8字節】和【結構體內部最大的基本數據類型成員】長度中數值較小者。結構體的長度應該是該模數的整數倍。
3.1 基本數據類型所占內存大小
以下例子均按32bit編譯器處理。
3.2 Test1
#pragma pack(4) struct Test1 { char c; short sh; int a; float f; int *p; char *s; double d; };
總共占28Bytes。 c的偏移量為0,占1個Byte。sh占2個Byte,它的對齊模數是2(2<4,取小者),存放起始地址應該是2的整數倍,因此c后填充1個空字符,sh的起始地址是2。a占4個Byte,對齊模數是4,因此接在sh后存放即可,偏移量為4。f占4個字節,對齊模數是4,存放地址是4的整數倍,起始地址是8。p,s的起始地址分別是12,16。d占8個字節,對齊模數是4(4<8),d從偏移地址為20處存放。存放后結構體占28個字節,是4的整數倍不用補空字符。
struct Test2 { char c; double d; int a; short sh; float f; int *p; char *s; };
將Test1個變量的順序換一下位置,結構體Test2占用內存32Byte,可見寫結構體時,將各個變量按所占內存從小到大排列所占結構體所占內存較小。
3.3關於靜態變量static
靜態變量的存放位置與結構體實例的存儲地址無關,是單獨存放在靜態數據區的,因此用siezof計算其大小時沒有將靜態成員所占的空間計算進來。
#pragma pack(4) struct Test3 { char c; short sh; int a; float f; int *p; char *s; double d; static double sb; static int sn; };
sizeof(Test3)=28
3.4關於類
空類是會占用內存空間的,而且大小是1,原因是C++要求每個實例在內存中都有獨一無二的地址。
(一)類內部的成員變量:
- 普通的變量:是要占用內存的,但是要注意對齊原則(這點和struct類型很相似)。
- static修飾的靜態變量:不占用內容,原因是編譯器將其放在全局變量區。
(二)類內部的成員函數:
- 普通函數:不占用內存。
- 虛函數:要占用4個字節,用來指定虛函數的虛擬函數表的入口地址。所以一個類的虛函數所占用的地址是不變的,和虛函數的個數是沒有關系的
#pragma pack(4) class cBase{};
sizeof(cBase)=1
- 3.4.1 不包含虛函數的類
#pragma pack(4) class CBase1 { private: char c; short sh; int a; public: void fOut(){ cout << "hello" << endl; } };
不包含虛函數時,對於類中的成員變量按結構體對齊方式處理,普通函數函數不占內存。sizeof(CBase1)=8
3.4.2 包含虛函數的類
#pragma pack(4) class CBase2 { private: char c; short sh; int a; public: virtual void fOut(){ cout << "hello" << endl; } };
包含虛函數時,類中需要保存虛函數表的入口地址指針,即需要多保存一個指針。這個值跟虛函數的個數多少沒有關系。sizeof(CBase2)=12
3.4.3 子類
子類所占內存大小是父類+自身成員變量的值。特別注意的是,子類與父類共享同一個虛函數指針,因此當子類新聲明一個虛函數時,不必在對其保存虛函數表指針入口。
#pragma pack(4) class CBase2 { private: char c; short sh; int a; public: virtual void fOut(){ cout << "virtual 1" << endl; } }; class cDerive :public CBase { private : int n; public: virtual void fPut(){ cout << "virtual 2"; } };
sizeof(cDerive)= sizeof(cBase)+sizeof(int n) = 16