sizeof
以字節(jié)形式給出操作數(shù)的存儲大小踊挠。通過本文我們可以了解 sizeof
的使用和計(jì)算方法凸丸。
功能
sizeof
以字節(jié)形式給出操作數(shù)的存儲大小怎棱。
sizeof
是C語言的一種單目操作符九默,如C語言的其他操作符 ++
蒸殿、 --
等择克,它并不是函數(shù)恬总。
sizeof
操作符以字節(jié)形式給出了其操作數(shù)的存儲大小。操作數(shù)可以是一個(gè)表達(dá)式或括在括號內(nèi)的類型名肚邢,操作數(shù)的存儲大小由操作數(shù)的類型決定壹堰。
使用語法
sizeof
有如下兩種使用方式:
-
用于數(shù)據(jù)類型的
sizeof
使用形式:sizeof(type)
。這里骡湖,數(shù)據(jù)類型必須用括號括住贱纠。如
sizeof(int)
。 -
用于變量的
sizeof
使用形式:sizeof(var_name)
或sizeof var_name
响蕴。變量名可以不用括號括住谆焊。如
sizeof (var_name)
,sizeof var_name
等都是正確形式。帶括號的用法更普遍浦夷,大多數(shù)程序員采用這種形式辖试。
注意: sizeof
操作符不能用于 函數(shù)類型 辜王、 不完全類型 或 位字段 。不完全類型指具有未知存儲大小的數(shù)據(jù)類型罐孝,如未知存儲大小的數(shù)組類型呐馆、未知內(nèi)容的結(jié)構(gòu)或聯(lián)合類型、void類型等莲兢。如 sizeof(max)
而此時(shí)變量 max
定義為 int max()
, sizeof(char_v)
而此時(shí) char_v
定義為 char char_v[MAX]
且 MAX
未知汹来, sizeof(void)
都不是正確形式。
計(jì)算方法
基本數(shù)據(jù)類型大小
sizeof
操作符的結(jié)果類型是 size_t
改艇,它在頭文件中 typedef
為 unsigned int
類型收班。該類型保證能容納實(shí)現(xiàn)所建立的最大對象的字節(jié)大小。
對于 int
, unsigned int
, short int
, unsigned short
, long int
, unsigned long
, float
, double
, long double
等基本類型的大小遣耍,沒有明確的規(guī)定闺阱,但是一般應(yīng)當(dāng)記住如下原則:
- ANSI C規(guī)定
char
類型一定是8位。 -
long
類型的長度和cpu字長一樣舵变。 -
int
長度沒有規(guī)定酣溃,但是不比short
短不比long
長,并且linux上支持的所有體系中int
長度目前都是32位纪隙。 -
short
和int
類似赊豌,目前l(fā)inux上長度都是16位。
例如绵咱,一般常見的基本類型大小信息如下(32位機(jī)Linux平臺):
char為1byte;
int為4byte;
unsigned int為4byte碘饼;
short int為2byte;
unsigned short為2byte悲伶;
long int為4byte艾恼;
unsigned long為4byte;
float為4byte麸锉;
double為8byte钠绍;
long double為12byte;
指針類型大小
當(dāng)操作數(shù)是指針時(shí)花沉, sizeof
依賴于編譯器柳爽。
例如Microsoft C/C++7.0中, near
類指針字節(jié)數(shù)為2碱屁, far
磷脯、 huge
類指針字節(jié)數(shù)為4。
一般Unix的指針字節(jié)數(shù)為4娩脾。
數(shù)組類型大小
當(dāng)操作數(shù)具數(shù)組類型時(shí)赵誓,其結(jié)果是數(shù)組的總字節(jié)數(shù)。
如果操作數(shù)是函數(shù)中的數(shù)組形參或函數(shù)類型的形參, sizeof
給出其指針的大小俩功。
結(jié)構(gòu)和聯(lián)合類型大小
聯(lián)合類型操作數(shù)的 sizeof
是其最大字節(jié)成員的字節(jié)數(shù)隘冲。結(jié)構(gòu)類型操作數(shù)的 sizeof
是其對象包括任何填充在內(nèi)的總字節(jié)數(shù)。
關(guān)于結(jié)構(gòu)大小和結(jié)構(gòu)變量存放地址绑雄,本文后面例子中將重點(diǎn)進(jìn)行介紹,確定方法依照編譯器所不同奥邮,大致規(guī)則如下:
- 成員變量在結(jié)構(gòu)中被聲明的順序和其在內(nèi)存中的存儲順序一致万牺。第一個(gè)結(jié)構(gòu)成員變量相對整個(gè)結(jié)構(gòu)的地址偏移為0。
- 結(jié)構(gòu)成員變量的偏移地址由編譯器中設(shè)置的"最大對齊參數(shù)"洽腺,以及該結(jié)構(gòu)變量的類型大小決定脚粟。
- 整個(gè)結(jié)構(gòu)的大小由最大結(jié)構(gòu)成員變量類型或編譯器中設(shè)置的"最大對齊參數(shù)"決定。
注:這里的最大對齊方式可以由不同的編譯器的特定關(guān)鍵字來設(shè)置蘸朋。默認(rèn)一般是cpu的字長核无,例如32位機(jī)器為4字節(jié)。
結(jié)構(gòu)成員藕坯、結(jié)構(gòu)變量的地址偏移和大小計(jì)算方法分別如下:
- 結(jié)構(gòu)成員變量偏移地址計(jì)算:偏移地址為該成員類型和最大對齊字節(jié)团南,兩者大小較小者的整數(shù)倍。若當(dāng)前偏移位置不滿足條件炼彪,那么就填充至滿足吐根,再存放存放該結(jié)構(gòu)成員。
- 結(jié)構(gòu)成員變量尺寸計(jì)算:就是該成員對應(yīng)類型的大小辐马。
- 結(jié)構(gòu)變量偏移地址計(jì)算:偏移地址為該結(jié)構(gòu)中最大成員大小和最大對齊字節(jié)大小兩者較小者的整數(shù)倍拷橘。
- 結(jié)構(gòu)變量尺寸計(jì)算:整個(gè)結(jié)構(gòu)大小為結(jié)構(gòu)中最大成員變量與最大對齊方式兩者較小者的整數(shù)倍,如果當(dāng)前所有成員大小之和小于這個(gè)整數(shù)倍就會(huì)添充直至達(dá)到要求喜爷。
總之冗疮,首先假定好最大字節(jié)對齊方式,然后確定每個(gè)成員起始地址(該成員類型大小以及最大字節(jié)對齊的較小者的倍數(shù))檩帐,最后確定結(jié)構(gòu)總大小(最大成員大小和最大字節(jié)對齊較小者的倍數(shù))术幔。簡而言之,先每個(gè)成員轿塔,再整體特愿,不滿足則填充。
舉例
下面是整理的說明VC到底怎么樣來存放結(jié)構(gòu)的(可以參見后面參考資料勾缭,這里沒有對其進(jìn)行嚴(yán)格的實(shí)踐)揍障,其它編譯器處理方式有所不同,但是大體都一致俩由。
例1:小的在后毒嫡,填充也少
結(jié)構(gòu)定義如下:
struct MyStruct
{
double dda1;
char dda;
int type
};
地址偏移和大小計(jì)算方式如下:
為上面的結(jié)構(gòu)分配空間的時(shí)候,VC根據(jù)成員變量出現(xiàn)的順序和對齊方式:
(1)先為第一個(gè)成員dda1分配空間兜畸,該成員類型為double努释,其起始地址跟結(jié)構(gòu)的起始地址相同(偏移量 0 為sizeof(double)的倍數(shù)),占用sizeof(double)=8個(gè)字節(jié)咬摇;
(2)接下來為第二個(gè)成員dda分配空間伐蒂,該成員類型為char,其想對于結(jié)構(gòu)的起始地址的偏移量為8(偏移量8為sizeof(char)的倍數(shù))肛鹏,該成員變量占用 sizeof(char)=1個(gè)字節(jié)逸邦;
(3)接下來為第三個(gè)成員type分配空間,該成員類型為int在扰,相對于結(jié)構(gòu)的起始地址的偏移量為9缕减,不是sizeof (int)=4的倍數(shù),為了滿足對齊方式對偏移量的約束問題芒珠,VC自動(dòng)填充3個(gè)字節(jié)(這三個(gè)字節(jié)沒有放什么東西)桥狡,這時(shí)下一個(gè)可以分配的地址對于結(jié)構(gòu)的起始地址的偏移量為12,剛好是sizeof(int)=4的倍數(shù)皱卓,所以把type存放在偏移量為12的地方裹芝,該成員變量占用sizeof(int)=4個(gè)字節(jié);
這時(shí)整個(gè)結(jié)構(gòu)的成員變量已經(jīng)都分配了空間好爬,總的占用的空間大小為:8+1+3+4=16局雄,剛好為結(jié)構(gòu)的字節(jié)邊界數(shù)(即結(jié)構(gòu)中占用最大空間的類型所占用的字節(jié)數(shù)sizeof(double)=8)的倍數(shù),所以沒有空缺的字節(jié)需要填充存炮。所以整個(gè)結(jié)構(gòu)的大小為:sizeof(MyStruct)=8+1+ 3+4=16炬搭,其中有3個(gè)字節(jié)是VC自動(dòng)填充的,沒有放任何有意義的東西穆桂。
例2:大的在后宫盔,填充也大
交換一下上面的MyStruct的成員變量的位置,使它變成下面的情況:
struct MyStruct
{
char dda;
double dda1;
int type
}享完;
這時(shí)候地址偏移和大小計(jì)算方式如下:
為上面的結(jié)構(gòu)分配空間的時(shí)候灼芭,VC根據(jù)成員變量出現(xiàn)的順序和對齊方式:
(1)先為第一個(gè)成員dda分配空間,該成員類型為char般又,其起始地址跟結(jié)構(gòu)的起始地址相同(偏移量 0 為sizeof(char)的倍數(shù))严卖,占用sizeof(char)=1個(gè)字節(jié)趟济;
(2)接下來為第二個(gè)成員dda1分配空間硕舆,該成員類型為double灌闺,其想對于結(jié)構(gòu)的起始地址的偏移量為1,不是sizeof (double)=7的倍數(shù)堕义,為了滿足對齊方式對偏移量的約束問題猜旬,VC自動(dòng)填充7個(gè)字節(jié)(這7個(gè)字節(jié)沒有放什么東西),這時(shí)下一個(gè)可以分配的地址對于結(jié)構(gòu)的起始地址的偏移量為8,剛好是sizeof(double)=8的倍數(shù)洒擦,所以把dda1存放在偏移量為8的地方椿争,該成員變量占用sizeof(double)=8個(gè)字節(jié);
(3)接下來為第三個(gè)成員type分配空間熟嫩,該成員類型為int秦踪,相對于結(jié)構(gòu)的起始地址的偏移量為16,剛好是sizeof(int)=4的倍數(shù)掸茅,所以把type存放在偏移量為16的地方洋侨,該成員變量占用sizeof(int)=4個(gè)字節(jié);
這時(shí)整個(gè)結(jié)構(gòu)的成員變量已經(jīng)都分配了空間倦蚪,總的占用的空間大小為:1+7+8+4=20,不滿足結(jié)構(gòu)的字節(jié)邊界數(shù)(即結(jié)構(gòu)中占用最大空間的類型所占用的字節(jié)數(shù)sizeof(double)=8)的倍數(shù)边苹,所以VC自動(dòng)填充4個(gè)字節(jié)陵且。綜上,整個(gè)結(jié)構(gòu)的大小為:sizeof(MyStruc)為1+7+8+4+4=24个束。其中總的有7+4=11個(gè)字節(jié)是VC自動(dòng)填充的慕购,沒有放任何有意義的東西。
例3:設(shè)置最大對齊參數(shù)茬底,節(jié)省空間
我們也可屏蔽掉變量默認(rèn)的對齊方式沪悲,自己設(shè)定變量的對齊方式。VC中提供了 #pragma pack(n)
來設(shè)定變量以n字節(jié)對齊方式阱表。
這里n字節(jié)對齊的意思是說:
(1)結(jié)構(gòu)成員變量存放地址偏移按照兩種情況確定:如果n大于等于該成員變量類型所占字節(jié)殿如,則其偏移量滿足默認(rèn)對齊方式(即以成員變量類型大小對齊);否則最爬,偏移量為n的倍數(shù)涉馁。
(2)結(jié)構(gòu)的總大小也按下面兩種情況確定:如果n大于等于最大的成員變量類型所占字節(jié),那么結(jié)構(gòu)的總大小為占用空間最大的成員變量所占用的空間數(shù)的倍數(shù)爱致;否則烤送,必須為n的倍數(shù)。
下面舉例說明其用法糠悯。
#pragma pack(push) //保存對齊狀態(tài)
#pragma pack(4)//設(shè)定為4字節(jié)對齊
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop)//恢復(fù)對齊狀態(tài)
這時(shí)候地址偏移和大小計(jì)算方式如下:
以上結(jié)構(gòu)的大小為16帮坚,為此分配空間的時(shí)候,
(1)首先為m1分配空間互艾,該成員類型為char试和,同時(shí)sizeof(char)為1,小于我們自己設(shè)定的4字節(jié),所以起始地址按照sizeof(char)=1對齊忘朝,和結(jié)構(gòu)起始地址一樣灰署,(偏移為0為sizeof(char)的倍數(shù)),m1占用1個(gè)字節(jié);
(2)接著開始為m4分配空間,該成員類型為double,同時(shí)sizeof(double)為8溉箕,大于我們自己設(shè)定的4字節(jié)晦墙,所以按照我們設(shè)置的4字節(jié)對齊,這時(shí)其偏移量為1肴茄,需要補(bǔ)足3個(gè)字節(jié)晌畅,使其偏移量為4,滿足為n=4的倍數(shù),m4占用8個(gè)字節(jié)寡痰;
(3)接著為m3分配空間抗楔,該成員類型為int,sizeof(int)為4拦坠,和設(shè)定的4字節(jié)一樣连躏,所以按照4字節(jié)對齊,這時(shí)其偏移量為12贞滨,正好滿足為4的倍數(shù)入热,m3占用4個(gè)字節(jié)。
這時(shí)已經(jīng)為所有成員變量分配了空間晓铆,對于整個(gè)結(jié)構(gòu)變量勺良,其中最大類型為double為8字節(jié),大于我們設(shè)定的4字節(jié)骄噪,所以結(jié)構(gòu)總大小采用n=4的倍數(shù)尚困,這里目前共分配了16個(gè)字節(jié)正好是4的倍數(shù),所以結(jié)構(gòu)大小為1+3+8+4=16链蕊。同理事甜,如果把上面的#pragma pack(4)改為#pragma pack(16),那么我們可以得到結(jié)構(gòu)的大小為24滔韵。
從上面的例子我們可以看出讳侨,調(diào)整結(jié)構(gòu)成員變量的位置,或者設(shè)置對齊方式奏属,都會(huì)影響該結(jié)構(gòu)變量的存儲效率跨跨。同時(shí)設(shè)置對齊方式可能會(huì)影響速度。
五囱皿、其他
主要指出幾個(gè)額外需要注意的地方勇婴。
sizeof
應(yīng)用在C++中的類和結(jié)構(gòu)的處理情況是相同的
但有兩點(diǎn)需要注意:
- 結(jié)構(gòu)或者類中的靜態(tài)成員不對結(jié)構(gòu)或者類的大小產(chǎn)生影響,因?yàn)殪o態(tài)變量的存儲位置與結(jié)構(gòu)或者類的實(shí)例地址無關(guān)嘱腥。
- 沒有成員變量的結(jié)構(gòu)或類的大小為1耕渴,因?yàn)楸仨毐WC結(jié)構(gòu)或類的每一個(gè)實(shí)例在內(nèi)存中都有唯一的地址。
經(jīng)過實(shí)踐對于Linux如下代碼分別輸出的情況
-
C語言源文件
main.c
內(nèi)容如下:#include struct mystruct { }; int main(int argc, char *argv[]) { printf("%d\n",sizeof(struct mystruct)); return 0; }
采用
gcc main.c
編譯之后齿兔,輸出結(jié)果顯示0
橱脸,即空結(jié)構(gòu)體大小為0础米。采用
g++ main.c
編譯之后,輸出結(jié)果顯示1
添诉,即空結(jié)構(gòu)體大小為1屁桑。 -
C++語言源文件
main.cpp
如下:#include class myclass { }; struct mystruct { }; int main(int argc, char *argv[]) { printf("%d,%d\n",sizeof(myclass),sizeof(struct mystruct)); return 0; }
采用 gcc main.cpp
無法編譯。
采用 g++ main.cpp
編譯之后栏赴,輸出結(jié)果顯示 1,1
即空類和結(jié)構(gòu)體大小均為1蘑斧。
參考資料
http://hi.baidu.com/ciw_blue/blog/item/28d74c3f6b134cea54e7239e.html