關(guān)于C/C++中的sizeof

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 改艇,它在頭文件中 typedefunsigned 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位纪隙。
  • shortint 類似赊豌,目前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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市须眷,隨后出現(xiàn)的幾起案子竖瘾,更是在濱河造成了極大的恐慌,老刑警劉巖花颗,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捕传,死亡現(xiàn)場離奇詭異,居然都是意外死亡扩劝,警方通過查閱死者的電腦和手機(jī)乐横,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來今野,“玉大人,你說我怎么就攤上這事罐农√跛” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵涵亏,是天一觀的道長宰睡。 經(jīng)常有香客問我,道長气筋,這世上最難降的妖魔是什么拆内? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮宠默,結(jié)果婚禮上麸恍,老公的妹妹穿的比我還像新娘。我一直安慰自己搀矫,他們只是感情好抹沪,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓤球,像睡著了一般融欧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卦羡,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天噪馏,我揣著相機(jī)與錄音麦到,去河邊找鬼。 笑死欠肾,一個(gè)胖子當(dāng)著我的面吹牛瓶颠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播董济,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼步清,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了虏肾?” 一聲冷哼從身側(cè)響起廓啊,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎封豪,沒想到半個(gè)月后谴轮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吹埠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年第步,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缘琅。...
    茶點(diǎn)故事閱讀 38,664評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粘都,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出刷袍,到底是詐尸還是另有隱情翩隧,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布呻纹,位于F島的核電站堆生,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏雷酪。R本人自食惡果不足惜淑仆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哥力。 院中可真熱鬧蔗怠,春花似錦、人聲如沸吩跋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钞澳。三九已至怠惶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間轧粟,已是汗流浹背策治。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工脓魏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人通惫。 一個(gè)月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓茂翔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親履腋。 傳聞我的和親對象是個(gè)殘疾皇子珊燎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容