1. sizeof是一個操作符,作用是返回一個對象或者類型所占的內(nèi)存字節(jié)數(shù)。
MSDN上的解釋為:
The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type (including aggregate types). This keyword returns a value of type size_t.
其返回值類型為size_t,在頭文件stddef.h中定義。這是一個依賴于編譯系統(tǒng)的值,一般定義為
typedef unsigned int size_t;
2. 一般的,在32位編譯環(huán)境中,sizeof(int)的取值為4。
3. 指針來存放地址的,那么它當然等于計算機內(nèi)部地址總線的寬度。所以在32位計算機中,一個指針變量的返回值必定是4(注意結(jié)果是以字節(jié)為單位),在64位系統(tǒng)中指針變量的sizeof結(jié)果為8。
指針變量的sizeof值與指針所指的對象沒有任何關(guān)系。
由于所有的指針變量所占內(nèi)存大小相等,因此MFC的消息結(jié)構(gòu)通過指向結(jié)構(gòu)體的指針參數(shù)傳遞。
4. 數(shù)組的sizeof值等于數(shù)組所占用的內(nèi)存字節(jié)數(shù)
如下求數(shù)組元素的個數(shù):
int n = sizeof( a1 ) / sizeof( a1[0] ); // 總長度/第一個元素的長度 a1代表數(shù)組
由于傳的參數(shù)是指針,故c3!=3:
void foo3(char a3[3])
{
int c3 = sizeof( a3 ); // c3 == 4 因為是指針
}
調(diào)用函數(shù)時,程序會在棧上分配一個大小為3的數(shù)組嗎?
不會!數(shù)組是“傳址”的,調(diào)用者只需將實參的地址傳遞過去。
所以a3自然為指針類型(char*),c3的值也就為4。
5. 結(jié)構(gòu)體的大小等于最后一個成員的偏移量加上其大小再加上末尾的填充字節(jié)數(shù)目。
結(jié)構(gòu)體的sizeof需要考慮需要字節(jié)對齊。
原因:這樣有助于加快計算機的取數(shù)速度,否則就得多花指令周期了。為此,編譯器默認會對結(jié)構(gòu)體進行處理,讓寬度為2的基本數(shù)據(jù)類型(short等)都位于能被2整除的地址上,讓寬度為4的基本數(shù)據(jù)類型(int等)都位于能被4整除的地址上,以此類推。這樣,兩個數(shù)中間就可能需要加入填充字節(jié),所以整個結(jié)構(gòu)體的sizeof值就增長了。
準則:字節(jié)對齊的細節(jié)和編譯器實現(xiàn)相關(guān),但一般而言,滿足三個準則:
1) 結(jié)構(gòu)體變量的首地址能夠被其最寬基本類型成員的大小所整除;
2) 結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如有需要編譯器會在最末一個成員之后加上填充字節(jié)(trailing padding)。
3) 結(jié)構(gòu)體每個成員相對于結(jié)構(gòu)體首地址的偏移量(offset)都是成員大小的整數(shù)倍,如有需要編譯器會在成員之間加上填充字節(jié)(internal adding);
例子:
struct S1
{
char c;
int i;
};
//sizeof(S1)=8;
struct S2
{
int i;
char c;
};
//sizeof(S2)=8;
struct S3
{
char c1;
S1 s;
char c2
};
//sizeof(S3)=16;
解釋一下S3。
第一步:S1的最寬簡單成員的類型為int,S3在考慮最寬簡單類型成員時是將S1“打散”看的,所以S3的最寬簡單類型為int,這樣,通過S3定義的變量,其存儲空間首地址需要被4整除,整個sizeof(S3)的值也應(yīng)該被4整除。
第二步:考慮偏移量。
c1的偏移量為0;
s的偏移量為4;//考慮了最寬,通過填充進行了對齊,否則偏移量應(yīng)該是1。
c2的偏移量為4+sizeof(s)=12;
第三步:算上c2的所占大小,目前所占空間是13。但是應(yīng)該被4整除。所以通過填充,sizeof是16。
通過上面的敘述,我們可以得到一個公式:
結(jié)構(gòu)體的大小等于最后一個成員的偏移量加上其大小再加上末尾的填充字節(jié)數(shù)目,即:
sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( trailing padding )
編譯器命令:
編譯器的pack指令用來調(diào)整結(jié)構(gòu)體對齊方式。不同編譯器名稱和用法略有不同,VC6中通過#pragma pack實現(xiàn),也可以直接修改/Zp編譯開關(guān)。
#pragma pack的基本用法為:
#pragma pack( n )
其中,n為字節(jié)對齊數(shù),其取值為1、2、4、8、16,默認是8,如果這個值比結(jié)構(gòu)體成員的sizeof值小,那么它就是該成員的偏移量。即結(jié)構(gòu)體成員的偏移量應(yīng)該取最小值,公式如下:offsetof( item ) = min( n, sizeof( item ) )
例子:
#pragma pack(push) // 將當前pack設(shè)置壓棧保存
#pragma pack(2)// 必須在結(jié)構(gòu)體定義之前使用
struct S1
{
char c;
int i;
};
struct S3
{
char c1;
S1 s;
char c2
};
#pragma pack(pop) // 恢復先前的pack設(shè)置
計算sizeof(S1)時,min(2, sizeof(i))的值為2,所以i的偏移量為2,加上sizeof(i)等于6,能夠被2整除,所以整個S1的大小為6。
同樣,對于sizeof(S3),s的偏移量為2,c2的偏移量為8,加上sizeof(c2)等于9,不能被2整除,添加一個填充字節(jié),所以sizeof(S3)等于10。
還有一點要注意,“空結(jié)構(gòu)體”(不含數(shù)據(jù)成員)的大小不為0,而是1。試想一個“不
占空間”的變量如何被取地址、兩個不同的“空結(jié)構(gòu)體”變量又如何得以區(qū)分呢?因此,編譯器為“空結(jié)構(gòu)體”變量分配一個字節(jié)的空間用于占位存儲。例子:
struct S5 { };
sizeof( S5 ); // 結(jié)果為1
6. 含有位域的結(jié)構(gòu)體的sizeof(不是很常用)
例子1:
struct BF1
{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};
其內(nèi)存布局為:
|_f1_|_f2__|____f3___|____|
|_|_|_|_|_|_ |_|_|_|_|_|_|_|_|_|_|
0 3 7 8 13 16
位域類型為char,第1個字節(jié)僅能容納下f1和f2,所以f2被壓縮到第1個字節(jié)中,而f3只
能從下一個字節(jié)開始。因此sizeof(BF1)的結(jié)果為2。
示例2:
struct BF2
{
char f1 : 3;
short f2 : 4;
char f3 : 5;
};
由于相鄰位域類型不同,在VC6中其sizeof為6,在Dev-C++中為2。
示例3:
struct BF3
{
char f1 : 3;
char f2;
char f3 : 5;
};
非位域字段穿插在其中,不會產(chǎn)生壓縮,在VC6和Dev-C++中得到的大小均為3。
7. 聯(lián)合體的sizeof
結(jié)構(gòu)體在內(nèi)存組織上是順序式的,聯(lián)合體則是重疊式,各成員共享一段內(nèi)存,所以整個聯(lián)合體的sizeof也就是每個成員sizeof的最大值。結(jié)構(gòu)體的成員也可以是復合類型,這里,復合類型成員是被作為整體考慮的。
所以,下面例子中,U的sizeof值等于sizeof(s)。
union U
{
int i;
char c;
S1 s;
};