摘要:
經常被計算結構體的sizeof給搞暈,於是找了個時間,靜下心來,搞定它。
一、為什么結構體計算這么亂?
答案是字節對齊,計算機存儲系統中以Byte為單位存儲數據,不同數據類型所占的空間不同,如:整型(int)數據占4個字節,字符型(char)數據占一個字 節,
短整型(short)數據占兩個字節,等等。計算機為了快速的讀寫數據,默認情況下將數據存放在某個地址的起始位置,如:整型數據(int)默認存儲 在地址能被
4整除的起始位置,字符型數據(char)可以存放在任何地址位置(被1整除),短整型(short)數據存儲在地址能被2整除的起始位置。這樣字節對齊有助於加快
計算機的取數速度,否則就得多花指令周期了。
二、字節對齊的細節和具體編譯器實現相關,但一般而言,滿足三個准則:
1. 結構體變量的首地址能夠被其最寬基本類型成員的大小所整除;
2. 結構體每個成員相對於結構體首地址的偏移量都是當前成員大小的整數倍,如有需要編譯器會在成員之間加上填充字節;
3. 結構體的總大小為結構體最寬基本類型成員大小的整數倍,如有需要編譯器會在最末一個成員之后加上填充字節。
說明:1、基本類型是指前面提到的像char、short、int、float、double這樣的內置數據類型;
2、對於復合數據類型,如結構體嵌套結構體,那么基本類型是指前面提到的像char、short、int、float、double這樣的內置數據類型;
3、我認為計算結構體大小的時候,主要用到准則2和准則3,對於准則1是編譯器自動完成的,不需要過多理會。
4、C++中類的可以看做是特殊的結構體,所以類的sizeof的計算和結構體是一樣的。
三、下面拿具體的程序來詳細說明:
1 // sizeof(結構體).cpp : 定義控制台應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include<iostream> 6 using namespace std; 7 8 struct A 9 { 10 char a; 11 int b; 12 }; 13 struct B 14 { 15 char a; 16 A a1; 17 char b; 18 }; 19 int _tmain(int argc, _TCHAR* argv[]) 20 { 21 cout<<sizeof(A)<<endl<<sizeof(B); 22 return 0; 23 }
上述程序的輸出結果是8和16.
下面結合前面給出的准則具體分析一下。
對於結構體A其基本成員類型有char和int兩種,最寬的是int占用4個字節,那么根據准則1,編譯器會自動為結果體A分配一個能被4整除的首地址,A的第一個成員char的首地址就是結構體A的首地址,即偏移量為0,接下來,下一個成員變量int首地址,如果不做處理的話,應該是相對於A的偏移量是1,這就不滿足准則2了,所以編譯器開始在char的后面填充3個字節,使得int相對於A的偏移量是4,來滿足准則2,然后結構體的總大小就是1(char)+3(填充)+4(int)=8,同時滿足了准則3,不用再填充了,所以sizeof(A)=8。
對於結構體B,其中包括了一個復合類型,查看基本類型的時候,要將其中的結構體A拆分成char和int兩種類型來看,所以結構體B中的基本數據類型是char,char,int,char,最寬的數據類型是int,編譯器會自動為B分配一個能被4整除的首地址,B的第一個成員char的首地址就是結構體B的首地址,即偏移量為0,接下來,下一個成員變量A的首地址,如果不做處理的話,應該是相對於B的偏移量是1,這就不滿足准則2了,所以編譯器開始在char的后面填充3個字節,使得成員A相對於所在結構體B的地址偏移量是4,來滿足准則2,這時加上B的長度8,B中最后一個成員char相對於B來說地址偏移量是8,能滿足准則2,所以不需要在A的后面填充字節,這時B的總長度是1(char)+3(填充)+8(A)+1(char)=13,不滿足准則3,所以還需要在最后一個char后面再添加3個字節,最后得到B的總大小是1(char)+3(填充)+8(A)+1(char)+3(填充)=16,所以sizeof(B)=16。
