深入理解計算機各種類型大小(sizeof)
睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接
1.4 C99標準規定,函數、不能確定類型的表達式以及位域(bit-field)成員不能被計算s
Cpp代碼
深入理解計算機各種類型大小(sizeof)
Cpp代碼

- // Example of the sizeof keyword
- size_t i = sizeof( int );
- struct align_depends {
- char c;
- int i;
- };
- size_t size = sizeof(align_depends); // The value of size depends on
- // the value set with /Zp or
- // #pragma pack
- int array[] = { 1, 2, 3, 4, 5 }; // sizeof( array ) is 20
- // sizeof( array[0] ) is 4
- size_t sizearr = // Count of items in array
- sizeof( array ) / sizeof( array[0] );
// Example of the sizeof keyword size_t i = sizeof( int ); struct align_depends { char c; int i; }; size_t size = sizeof(align_depends); // The value of size depends on // the value set with /Zp or // #pragma pack int array[] = { 1, 2, 3, 4, 5 }; // sizeof( array ) is 20 // sizeof( array[0] ) is 4 size_t sizearr = // Count of items in array sizeof( array ) / sizeof( array[0] );
<!----> <script src="ms-its:dsmsdn.chm::/html/msdn_footer.js"></script> <!---->
1. 用法
1.1 sizeof和new、delete等一樣,是關鍵字,不是函數或者宏。
1.2 sizeof返回內存中分配的字節數,它和操作系統的位數有關。例如在常見的32位系統中,int類型占4個字節;但是在16位系統中,int類型占2個字節。
1.3 sizeof的參數可以是類型,也可以是變量,還可以是常量。對于相同類型,以上3中形式參數的sizeof返回值相同。
Cpp代碼

- int a;
- sizeof(a); // = 4
- sizeof(int); // = 4
- sizeof(1); // = 4
int a; sizeof(a); // = 4 sizeof(int); // = 4 sizeof(1); // = 4
1.4 C99標準規定,函數、不能確定類型的表達式以及位域(bit-field)成員不能被計算s
izeof值,即下面這些寫法都是錯誤的。
Cpp代碼 

- void fn() { }
- sizeof(fn); // error:函數
- sizeof(fn()); // error:不能確定類型
- struct S
- {
- int a : 3;
- };
- S sa;
- sizeof( sa.a ); // error:位域成員
void fn() { } sizeof(fn); // error:函數 sizeof(fn()); // error:不能確定類型 struct S { int a : 3; }; S sa; sizeof( sa.a ); // error:位域成員
1.5 sizeof在編譯階段處理。由于sizeof不能被編譯成機器碼,所以sizeof的參數不能被編譯,而是被替換成類型。
Cpp代碼

- int a = -1;
- sizeof(a=3); // = sizeof(a) = sizeof(int) = 4
- cout<<a<<endl; // 輸出-1。由于“=”操作符返回左操作數的類型,賦值操作沒有執行。
int a = -1; sizeof(a=3); // = sizeof(a) = sizeof(int) = 4 cout<<a<<endl; // 輸出-1。由于“=”操作符返回左操作數的類型,賦值操作沒有執行。
2. 在32位系統中不同類型的內存分配
2.1 基本類型
Cpp代碼

- sizeof(int); // = 4
- sizeof(double); // = 8
- sizeof(char); // = 1
- sizeof(bool); // = 1
- sizeof(short); // = 2
- sizeof(float); // = 4
- sizeof(long); // = 4
sizeof(int); // = 4 sizeof(double); // = 8 sizeof(char); // = 1 sizeof(bool); // = 1 sizeof(short); // = 2 sizeof(float); // = 4 sizeof(long); // = 4
2.2 指針
指針在32位系統中占4個字節。
Cpp代碼

- sizeof(int *); // = 4
- sizeof(double *); // = 4
- sizeof(char *); // = 4
sizeof(int *); // = 4 sizeof(double *); // = 4 sizeof(char *); // = 4
2.3 數組
2.3.1 數組的sizeof返回整個數組所占的字節數,即(數組元素個數×每個元素所占字節)。
Cpp代碼

- int ai[] = {1, 2};
- sizeof(ai); // = 2*4 = 8
int ai[] = {1, 2}; sizeof(ai); // = 2*4 = 8
2.3.2 常量字符串與字符數組的內存分配方式相同。
Cpp代碼

- char ac[] = "abcd"; //注意數組末尾的字符串終結符'\0'
- sizeof(ac); // = 5*1 = 5
- sizeof("abcd"); // = 5*1 = 5
char ac[] = "abcd"; //注意數組末尾的字符串終結符'\0' sizeof(ac); // = 5*1 = 5 sizeof("abcd"); // = 5*1 = 5
2.3.3 數組和指針所占的字節數不同,應注意區分。
Cpp代碼

- int *pi = new int[10]; //這是指針
- sizeof(pi); // = 4
- int ai[10];
- int *p = ai; //這還是指針
- sizeof(p); // = 4
- double* (*a)[3][6]; //看成(double *) (*a)[3][6],即一個3×6的二維數組,數組元素為指針,指向double類型。
- sizeof(a); // = 4,a為指向上述二維數組的指針
- sizeof(*a); // = sizeof(double *)*3*6 = 72,*a表示上述二維數組
- sizeof(**a); // = sizeof(double *)*6 = 24,**a即*(*a),表示double*[6],是元素為double指針的一維數組。
- sizeof(***a); // = sizeof(double *) = 4,表示上述一維數組中的第一個元素,元素類型為double指針。
- sizeof(****a); // = sizeof(double) = 8,表示上述數組首元素指向的double類型。
int *pi = new int[10]; //這是指針 sizeof(pi); // = 4 int ai[10]; int *p = ai; //這還是指針 sizeof(p); // = 4 double* (*a)[3][6]; //看成(double *) (*a)[3][6],即一個3×6的二維數組,數組元素為指針,指向double類型。 sizeof(a); // = 4,a為指向上述二維數組的指針 sizeof(*a); // = sizeof(double *)*3*6 = 72,*a表示上述二維數組 sizeof(**a); // = sizeof(double *)*6 = 24,**a即*(*a),表示double*[6],是元素為double指針的一維數組。 sizeof(***a); // = sizeof(double *) = 4,表示上述一維數組中的第一個元素,元素類型為double指針。 sizeof(****a); // = sizeof(double) = 8,表示上述數組首元素指向的double類型。
2.3.4 函數形式參數中的數組會蛻變為指針,原因是數組參數“傳址調用”,調用者只需將實參的地址傳遞過去。有一種情況例外,那就是參數是指向數組的指針。
Cpp代碼

- void acf(char p[3]) //參數類型是int[],表示指向int的指針
- {
- sizeof( p ); // = 4
- }
- void aif(int p[]) //參數類型是int[],表示指向int的指針
- {
- sizeof( p ); // = 4
- }
- void pif(int (*p)[6]) //參數類型是int (*)[6],表示指向int數組的指針
- {
- sizeof( p); // = 4
- sizeof( *p ); // = sizeof(int)*6 = 24
- }
- void ppf(int *p[6]) //參數類型是int *[],表示指向int指針的指針
- {
- sizeof( p ); // = 4
- sizeof( *p ); // = 4
- }
void acf(char p[3]) //參數類型是int[],表示指向int的指針 { sizeof( p ); // = 4 } void aif(int p[]) //參數類型是int[],表示指向int的指針 { sizeof( p ); // = 4 } void pif(int (*p)[6]) //參數類型是int (*)[6],表示指向int數組的指針 { sizeof( p); // = 4 sizeof( *p ); // = sizeof(int)*6 = 24 } void ppf(int *p[6]) //參數類型是int *[],表示指向int指針的指針 { sizeof( p ); // = 4 sizeof( *p ); // = 4 }
2.4. 類和結構體的內存分配。
2.4.1 空類或空結構體占一個字節。
Cpp代碼

- class CEmpty { };
- sizeof(CEmpty); // = 1
- struct SEmpty { };
- sizeof(SEmpty); // = 1
class CEmpty { }; sizeof(CEmpty); // = 1 struct SEmpty { }; sizeof(SEmpty); // = 1
2.4.2 非空類和結構體所占字節為所有成員占字節的和,但是不包括成員函數和靜態成員所占的空間。
Cpp代碼

- class CInt : public CEmpty {
- int i;
- };
- sizeof(CInt); // = 4;
- class CFunc {
- void f() {}
- };
- sizeof(CFunc); // = 1;
- struct SInt : SEmpty {
- static int i;
- };
- sizeof(SInt); // = 1;
class CInt : public CEmpty { int i; }; sizeof(CInt); // = 4; class CFunc { void f() {} }; sizeof(CFunc); // = 1; struct SInt : SEmpty { static int i; }; sizeof(SInt); // = 1;
2.4.3 字節對齊
為了加快計算機的取數速度,編譯器默認對內存進行字節對齊。對結構體(包括類)進行字節對齊的原則是:
1)結構體變量的首地址能夠被其最寬基本類型成員的大小所整除;
2)結構體每個成員相對于結構體首地址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充字節(internal adding);
3)結構體的總大小為結構體最寬基本類型成員大小的整數倍,如有需要編譯器會在最末一個成員之后加上填充字節(trailing padding)。
Cpp代碼

- struct SByte1
- {
- double d; // 偏移量0~7
- char j; // 偏移量8
- int a; // 偏移量12~15,由于9不能整除4,故先填充9~11
- };
- sizeof(SByte1); // = 16
- struct SByte2
- {
- char j; // 偏移量0
- double d; // 偏移量8~15,由于1不能整除8,故先填充1~7
- int a; // 偏移量16~19
- };
- sizeof(SByte2); // = 24,為了湊成8的倍數,填充20~23
struct SByte1 { double d; // 偏移量0~7 char j; // 偏移量8 int a; // 偏移量12~15,由于9不能整除4,故先填充9~11 }; sizeof(SByte1); // = 16 struct SByte2 { char j; // 偏移量0 double d; // 偏移量8~15,由于1不能整除8,故先填充1~7 int a; // 偏移量16~19 }; sizeof(SByte2); // = 24,為了湊成8的倍數,填充20~23
另外,可以通過#pragma pack(n)來設定變量以n字節對齊方式。
Cpp代碼

- #pragma pack(push) //保存對齊狀態
- #pragma pack(4) //設定為4字節對齊
- class CByte
- {
- char c; //偏移量0
- double d; //偏移量4~11,由于1不能整除4,故先填充1~3
- int i; //偏移量12~15
- };
- #pragma pack(pop) //恢復對齊狀態
- sizeof(CByte); // = 16
#pragma pack(push) //保存對齊狀態 #pragma pack(4) //設定為4字節對齊 class CByte { char c; //偏移量0 double d; //偏移量4~11,由于1不能整除4,故先填充1~3 int i; //偏移量12~15 }; #pragma pack(pop) //恢復對齊狀態 sizeof(CByte); // = 16
2.4.4 位域
有些信息在存儲時,并不需要占用一個完整的字節, 而只需占幾個或一個二進制位。例如在存放一個開關量時,只有0和1 兩種狀態,用一位二進位即可。為了節省存儲空間,并使處理簡便,C語言又提供了一種數據結構,稱為“位域”或“位段”。所謂“位域”是把一個字節中的二進位劃分為幾個不同的區域, 并說明每個區域的位數。
2.4.4.1 位域以比特位作為單位,其長度不能大于一個字節。一個位域必須存儲在同一個字節中,如一個字節所剩空間不夠存放另一位域時,應從下一單元起存放該位域。
Cpp代碼

- struct SBit1
- {
- char a : 3;
- char b : 4;
- char c : 5;
- };
- sizeof(SBit1); // = (3+4+1+5+3) bits = 2 bytes
struct SBit1 { char a : 3; char b : 4; char c : 5; }; sizeof(SBit1); // = (3+4+1+5+3) bits = 2 bytes
SBit1:| a × 3+ b × 4 + # × 1 | c × 5 + # × 3 |
2.4.4.2 使用空域可以有意使某位域從下一單元開始,但是空域不能使用。
Cpp代碼

- struct SBit2
- {
- char a : 3;
- char : 0; // 空域
- char b : 4;
- char c : 5;
- };
- sizeof(SBit2); // = (3+4+1+5+3) bits = 3 bytes
struct SBit2 { char a : 3; char : 0; // 空域 char b : 4; char c : 5; }; sizeof(SBit2); // = (3+4+1+5+3) bits = 3 bytes
SBit2:| a ×3 + # × 5 | b × 4 + # × 4 | c × 5 + # × 3 |
2.4.4.3 如果相鄰的位域字段的類型不同,則各編譯器的具體實現有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式。
Cpp代碼

- struct SBit3
- {
- char a : 3;
- short b : 4;
- char c : 5;
- };
- sizeof(SBit3); // = 6 bytes,由于相鄰位域類型不同,在VC6中其sizeof為6,在Dev-C++中為2。
struct SBit3 { char a : 3; short b : 4; char c : 5; }; sizeof(SBit3); // = 6 bytes,由于相鄰位域類型不同,在VC6中其sizeof為6,在Dev-C++中為2。
SBit3(不壓縮):| a ×3 | # ×8 |b × 4 + # ×4 | # ×8 | c ×5 + # ×3 | # ×8 |
SBit3(壓縮):| a×3 + b ×4 + # ×1 | c ×5 + # ×3 |
2.4.4.4 如果位域字段之間穿插著非位域字段,則不進行壓縮。
Cpp代碼

- struct SBit4
- {
- int a : 3;
- int b : 4;
- int c;
- };
- sizeof(SBit4); // = 8 bytes
struct SBit4 { int a : 3; int b : 4; int c; }; sizeof(SBit4); // = 8 bytes
SBit4:| a×3 + b ×4 + # ×1 | # ×8 | # ×8 | # ×8 | c ×8 | c ×8 | c ×8 | c ×8 |
2.4.4.5 整個結構體的總大小為最寬基本類型成員大小的整數倍。
Cpp代碼

- struct SBit5
- {
- int a : 3;
- int b;
- int c : 5;
- };
- sizeof(SBit5); // = 12 bytes
struct SBit5 { int a : 3; int b; int c : 5; }; sizeof(SBit5); // = 12 bytes
SBit5:| a×3 + # ×5 | # ×8 | # ×8 | # ×8 | b ×8 | b ×8 | b ×8 | b ×8 | c ×5 + # ×3 | # ×8 | # ×8 | #×8 |
2.5 聯合
聯合表示若干數據成員取其一,故以疊加方式分配內存,所占字節數為最大數據成員所占的字節數。
Cpp代碼

- union U
- {
- int i;
- char c;
- double d;
- };
- sizeof(U); // = Max(sizeof(i), sizeof(c), sizeof(d)) = sizeof(d) = 8