從sizeof剖析C++中的內存分配

sizeof Operator
sizeof expression 


 


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 .


 


The expression is either an identifier or a type-cast expression (a type specifier enclosed in parentheses).


 


When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment. When applied to a statically dimensioned array, sizeof returns the size of the entire array. The sizeof operator cannot return the size of dynamically allocated arrays or external arrays.


 


For related information, see Operators .


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 bytesSBit1:| 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 bytesSBit2:| 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  

轉自http://www.iteye.com/topic/194299

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章