全面總結sizeof的用法(定義、語法、指針變量、數組、結構體、類、聯合體、位域位段)


一、前言

編譯環境是vs2010(32位)。

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">#include<iostream>  
  2.     #include<stdio.h>  
  3.     #include<string.h>  
  4. using namespace std;  
  5. typedef struct  
  6. {  
  7.     int a;  
  8.     char b;  
  9. }A_t;  
  10. typedef struct  
  11. {  
  12.     int a;  
  13.     char b;  
  14.     char c;  
  15. }B_t;  
  16. typedef struct  
  17. {  
  18.     char a;  
  19.     int b;  
  20.     char c;  
  21. }C_t;  
  22. void main()  
  23. {  
  24.     char*a=0;  
  25.     cout<<sizeof(<span style="color:#ff0000;">a</span>)<<endl;//4  
  26.     cout<<sizeof(<span style="color:#ff0000;">*a</span>)<<endl;//1--這個能理解  
  27.     cout<<sizeof(<span style="color:#ff0000;">A_t</span>)<<endl;//8  
  28.     cout<<sizeof(<span style="color:#ff0000;">B_t</span>)<<endl;//8  
  29.     cout<<sizeof<span style="color:#ff0000;">(C_t</span>)<<endl;//12  
  30. }</span>  

 

為什么是這樣的結果啊?

 

二、語法

sizeof有三種語法形式,如下: 

1) sizeof( object ); // sizeof( 對象 ); 

2) sizeof( type_name ); // sizeof( 類型 ); 

3) sizeof object; // sizeof 對象; 

三. 指針變量的sizeof 

既然是來存放地址的,那么它當然等於計算機內部地址總線的寬度。所以在32位計算機中,一個指針變量的返回值必定是4(以字節為單位),在64位系統中指針變量的sizeof結果為8。 

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">#include<iostream>  
  2. #include<stdio.h>  
  3. #include<string.h>  
  4. using namespace std;  
  5. int main()  
  6. {  
  7. char *a=0;  
  8. char* pc = "abc";   
  9. int* pi;   
  10. string* ps;   
  11. char** ppc = &pc;   
  12. <span style="color:#ff0000;">void (*pf)();// 函數指針</span>  
  13. cout<<sizeof(<span style="color:#ff0000;">char</span>)<<endl; //1  
  14. cout<<sizeof(<span style="color:#ff0000;">a</span>)<<endl;//4  
  15. cout<<sizeof(<span style="color:#ff0000;">*a</span>)<<endl;//1  
  16. cout<<sizeof(<span style="color:#ff0000;">pc</span>)<<endl; //4(指針)  
  17. cout<<sizeof(<span style="color:#ff0000;">pi</span>)<<endl;//4(指針)  
  18. cout<<sizeof(<span style="color:#ff0000;">ps</span>)<<endl; //4(string型指針)  
  19. cout<<sizeof(<span style="color:#ff0000;">ppc</span>)<<endl; //4(指針)  
  20. cout<<sizeof(<span style="color:#ff0000;">pf</span>)<<endl;//4  
  21. }</span>  

 

 

指針變量的sizeof值與指針所指的對象沒有任何關系,正是由於所有的指針變量所占內存

大小相等,所以MFC消息處理函數使用兩個參數WPARAM、LPARAM就能傳遞各種復雜的消息結構(使用指向結構體的指針)。 

 

四.、數組的sizeof 

數組的sizeof值等於數組所占用的內存字節數,如: 

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">#include<iostream>  
  2. #include<stdio.h>  
  3. #include<string.h>  
  4. using namespace std;  
  5. int main()  
  6. {  
  7. char b1[]="123";  
  8. int b2[3];  
  9. <span style="color:#ff0000;">int c1=sizeof(b1)/sizeof(char);  
  10. int c2=sizeof(b1)/sizeof(b1[0]);  
  11. int c3=sizeof(b2)/sizeof(int);</span>  
  12.      int c4=sizeof(b2)/sizeof(b2[0]);  
  13. cout<<sizeof(b1)<<' '<<c1<<' '<<c2<<endl;//4 4 4  
  14. cout<<sizeof(b2)<<' '<<c3<<' '<<c4<<endl;//12(3*4 依賴int) 3 3  
  15. }</span>  

 

1.數組長度

char a1[] = "abc"; 

int a2[3]; 

sizeof( a1 ); // 結果為4,字符串末尾還存在一個NULL終止符 

sizeof( a2 ); // 結果為3*4=12(依賴於int) 

2.數組元素個數

int c1 = sizeof( a1 ) / sizeof( char ); // 總長度/單個元素的長度 

int c2 = sizeof( a1 ) / sizeof( a1[0] ); // 總長度/第一個元素的長度 

 

3.數組“傳址”(數組為函數參數)

我們可以思考一下,下面的c3,c4值應該是多少呢? 

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">void foo3(char a3[3])   
  2. {   
  3. int c3 = sizeof( a3 ); // c3 ==   
  4. }   
  5. void foo4(char a4[])   
  6. {   
  7. int c4 = sizeof( a4 ); // c4 ==   
  8. } </span>  

 

也許當你試圖回答c4的值時已經意識到c3答錯了,是的,c3!=3。這里函數參數a3已不再是數組類型,而是蛻變成指針,相當於char* a3,為什么?仔細想想就不難明白,我們調用函數foo1時,程序會在棧上分配一個大小為3的數組嗎?不會!數組是“傳址”的,調用者只需將實參的地址傳遞過去,所以a3自然為指針類型(char*),c3的值也就為4。 

五. 結構體的sizeof 

結構體相對而言最容易碰到而且最容易出錯。讓我們先看一個結構體:

 

struct S1
{
char c;
int i;
};

 

編譯得到結果為8! 

我們來好好琢磨一下sizeof的定義——sizeof的結果等於對象或者類型所占的內存字節數,好吧,那就讓我們來看看S1的內存分配情況: 

S1 s1 = {  a , 0xFFFFFFFF }; 

定義上面的變量后,加上斷點,運行程序,觀察s1所在的內存,你發現了什么? 

以我的Vs為例,s1的地址為0x0012FF78,其數據內容如下: 

0012FF78: 61 CC CC CC FF FF FF FF 

發現了什么?怎么中間夾雜了3個字節的CC?看看MSDN上的說明: 

When applied to a structure type or variable, sizeof returns the actual size, 

which may include padding bytes inserted for alignment. 

原來如此,這就是傳說中的字節對齊啊!一個重要的話題出現了。 

1.怎么判斷內存對齊規則,sizeof的結果怎么來的,牢記如下3條規則(在沒有#pragma pack宏的情況下):

(1)數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset為0的地方,以后每個數據成員存儲的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數組,結構體等)的整數倍開始(比如int在32位機為4字節,則要從4的整數倍地址開始存儲)。

(2)結構體作為成員:如果一個結構體里有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲(struct a 里存有struct b,b里有char,int,double等元素,那么b應該從8的整數倍開始存儲)。

(3)收尾工作:結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。

  • 類型

    對齊方式(變量存放的起始地址相對於結構的起始地址的偏移量)

    Char

    偏移量必須為sizeof(char)即1的倍數

    int

    偏移量必須為sizeof(int)即4的倍數

    float

    偏移量必須為sizeof(float)即4的倍數

    double

    偏移量必須為sizeof(double)即8的倍數

    Short

    偏移量必須為sizeof(short)即2的倍數

 

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">#include<iostream>  
  2. #include<stdio.h>  
  3. #include<string.h>  
  4. using namespace std;  
  5. typedef struct bb  
  6. {  
  7.  int id;             //[0]....[3]  
  8.  double weight;      //[8].....[15]      原則1  
  9.  float height;      //[16]..[19],總長要為8的整數倍,補齊[20]...[23]     原則3  
  10. }BB;  
  11. typedef struct aa  
  12. {  
  13. char name[2];  //[0] [1]  
  14. int id;     //[4]....[7]   原則1  
  15. double score;// [8]...[15]   
  16. short t;    //[16]...[17]  
  17. BB b;   //[24]...[47]  原則2、3  
  18. }AA;  
  19. int main()  
  20. {  
  21. cout<<sizeof(BB)<<endl; //為24  
  22. cout<<sizeof(AA)<<endl; //為48  
  23. return 0;  
  24. }</span>  

 

 

2.帶#pragma pack()

在代碼前加上一句#pragma pack(1),bb就是4+8+4=16。Aa就是2+4+8+2+16=32.  

 

六、聯合體的sizeof 

結構體在內存組織上是順序式的,聯合體則是重疊式,各成員共享一段內存,所以整個聯合體的sizeof也就是每個成員sizeof的最大值。結構體的成員也可以是復合類型,這里,復合類型成員是被作為整體考慮的。 

所以,下面例子中,U的sizeof值等於sizeof(s)。 

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">union U   
  2. {   
  3. int i;   
  4. char c;   
  5. AA s;   
  6. };</span>  

 

七、類的sizeof

1、空類的sizeof是1。空類是指沒有成員的類,類中的函數不占空間,除非是虛函數。

如:

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;"> class A  
  2.         {  
  3.    
  4.              public:  
  5.                       A(){}  
  6.    
  7.                      ~A(){}  
  8.    
  9.                      void fun(){}  
  10.          };</span>  

 

sizeof(A)是1.

注:

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;"> class A1  
  2.         {  
  3.              public:  
  4.                       A1(){}  
  5.                      ~A1(){}  
  6.                      void fun(){}  
  7.                        char a[0];  
  8.          };</span>  
  9.    

 

sizeof(A1)也是1.(VC6.0下編譯)

 

2、若類中包含成員,則類對象的大小只包括其中非靜態成員經過對齊所占的空間,對齊方式和結構體相同。如:

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">class A  
  2. {  
  3. public:  
  4.   int b;  
  5.   float c;  
  6.   char d;  
  7. };</span>  

 

sizeof(A)是12.

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">class A  
  2. {  
  3. public:  
  4.   static int a;  
  5.   int b;  
  6.   float c;  
  7.   char d;  
  8. };</span>  

 

sizeof(A)是12.

 

3、若類中包含虛函數,則無論有幾個虛函數,sizeof類都等於sizeof(數據成員)的和+sizeof(V表指針,為4),如:

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">class Base  
  2. {  
  3.       public:  
  4.              Base(){cout<<"Base-ctor"<<endl;}  
  5.              ~Base(){cout<<"Base-dtor"<<endl;}  
  6.              int a;  
  7.              virtual void f(int) {cout<<"Base::f(int)"<<endl;}  
  8.              virtual void f(double){cout<<"Base::f(double)"<<endl;}//共有兩個虛擬函數(virtual)  
  9. };</span>  

 

 

sizeof(Base)為8.

 

4、對於子類,它的sizeof是它父類成員(無論成員是public或private),再加上它自己的成員,對齊后的sizeof,如:

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">class A2  
  2. {  
  3.       public:  
  4.              int a;  
  5.       private:  
  6.               char b;  
  7. };  
  8.    
  9. class A3:public A2  
  10. {  
  11.       public:  
  12.              char b;  
  13.              short a;          }</span>  

 

sizeof(A3)是12.

 

5、對於子類和父類中都有虛函數的情況,子類的sizeof是它父類成員(無論成員是public或private),再加上它自己的成員,對齊后的sizeof,再加4(虛表指針)。如:

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">class Base  
  2. {  
  3.       public:  
  4.              Base(){cout<<"Base-ctor"<<endl;}  
  5.              ~Base(){cout<<"Base-dtor"<<endl;}  
  6.              int a;  
  7.              virtual void f(int) {cout<<"Base::f(int)"<<endl;}  
  8.              virtual void f(double){cout<<"Base::f(double)"<<endl;}  
  9. };  
  10.    
  11. class Derived:public Base  
  12. {  
  13.   public:  
  14.          Derived(){cout<<"Derived-ctor"<<endl;}  
  15.          int b;  
  16.          virtual void g(int){cout<<"Derived::g(int)"<<endl;}  
  17. };</span>  

 

sizeof(Derived)是12.

6、對於虛繼承的子類,其sizeof的值是其父類成員,加上它自己的成員,以及它自己一個指向父類的指針(大小為4),對齊后的sizeof。如:

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">#include   <iostream.h>      
  2.   class   a     
  3.   {     
  4.   private:     
  5.   int   x;     
  6.   };     
  7.       
  8.   class   b:   virtual   public   a     
  9.   {     
  10.   private:     
  11.   int   y;     
  12.   };       
  13.   class   c:   virtual   public   a     
  14.   {     
  15.   private:     
  16.   int   z;     
  17.   };     
  18.   class d:public   b,public   c     
  19.   {     
  20.   private:     
  21.   int   m;     
  22.   };     
  23.   int main(int argc,char* argv[])     
  24.   {     
  25.   cout<<sizeof(<span style="color:#ff0000;">a</span>)<<endl;     
  26.   cout<<sizeof(<span style="color:#ff0000;">b</span>)<<endl;     
  27.   cout<<sizeof(<span style="color:#ff0000;">c</span>)<<endl;     
  28.   cout<<sizeof(<span style="color:#ff0000;">d</span>)<<endl;     
  29.   return   0;     
  30.   }   </span>  

 

 

    在VC6.0下調試結果為   

  4   

  12   

  12   

  24

sizeof(b)和sizeof(c)相同,都是4+4+4=12。

sizeof(d)是sizeof(b)(為12)+sizeof(c)(為12)-b和c相同的部分(a的成員,大小是4)+d自己的成員(大小為4)=24

 

7、對於既有虛繼承又有虛函數的子類,其sizeof的值是其父類成員(計算虛表指針大小+4),加上它自己的成員(計算虛表指針大小+4),以及它自己一個指向父類的指針(大小為4),對齊后的sizeof。

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">class Base  
  2. {  
  3. public:  
  4.  Base(){cout<<"Base-ctor"<<endl;}  
  5.  ~Base(){cout<<"Base-dtor"<<endl;}  
  6.  virtual void f(int) {cout<<"Base::f(int)"<<endl;}  
  7. virtual void f(double){cout<<"Base::f(double)"<<endl;}  
  8. };  
  9.    
  10. class Derived:virtual public Base  
  11. {  
  12. public:  
  13.  Derived(){cout<<"Derived-ctor"<<endl;}  
  14.  virtual void g(int){cout<<"Derived::g(int)"<<endl;}  
  15. };</span>  

 

sizeof(Base)=4

sizeof(Derived)=12 (父類虛表指針大小4+自己虛表指針大小4+子類指向父類的一個指針大小4=12)

 

七、C結構體之位域(位段)的sizeof

    有些信息在存儲時,並不需要占用一個完整的字節, 而只需占幾個或一個二進制位。例如在存放一個開關量時,只有0和1 兩種狀態, 用一位二進位即可。為了節省存儲空間,並使處理簡便,C語言又提供了一種數據結構,稱為“位域”或“位段”。所謂“位域”是把一個字節中的二進位划分為幾個不同的區域, 並說明每個區域的位數。每個域有一個域名,允許在程序中按域名進行操作。 這樣就可以把幾個不同的對象用一個字節的二進制位域來表示。

(一)位域的定義和位域變量的說明位域定義與結構定義相仿,其形式為: 

 

struct 位域結構名 

{

 

 位域列表

 

};

其中位域列表的形式為:

 

類型說明符 位域名:位域長度

 

位域變量的說明與結構變量說明的方式相同。 可采用先定義后說明,同時定義說明或者直接說明這三種方式。例如:

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">struct bs  
  2. {  
  3.   int a:8;  
  4.   int b:2;  
  5.   int c:6;  
  6. }data; </span>  

 

 

說明data為bs變量,共占兩個字節。其中位域a占8位,位域b占2位,位域c占6位。對於位域的定義尚有以下幾點說明:

 

1. 一個位域必須存儲在同一個字節中,不能跨兩個字節。如一個字節所剩空間不夠存放另一位域時,應從下一單元起存放該位域。也可以有意使某位域從下一單元開始。例如:

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">struct bs  
  2. {  
  3.     unsigned a:4  
  4.     unsigned b:5 /*從下一單元開始存放*/  
  5.     unsigned c:4  
  6. }</span>  

 

2. 由於位域不允許跨兩個字節,因此位域的長度不能大於一個字節的長度。

3. 位域可以無位域名,這時它只用來作填充或調整位置。無名的位域是不能使用的。例如:

 

[cpp]  view plain  copy
 
  1. <span style="font-size:18px;">struct k  
  2. {  
  3.     int a:1  
  4.     int :2 /*無位域名,該2位不能使用*/  
  5.     int b:3  
  6.     int c:2  
  7. }; </span>  

 

 

(二)位域的使用

 

[cpp]  view plain  copy
 
  1.  <span style="font-size:18px;">#include <iostream>  
  2. #include <memory.h>  
  3.  using namespace std;  
  4.  struct A  
  5.  {  
  6.      int a:5;  
  7.     int b:3;  
  8.  };  
  9.  int main(void)  
  10. {  
  11.     char str[100] = "0134324324afsadfsdlfjlsdjfl";  
  12.         struct A d;  
  13.     memcpy(&d, str, sizeof(A));  
  14.     cout << d.a << endl;  
  15.     cout << d.b << endl;  
  16.     return 0;  
  17. }</span>  

 

復制代碼

在32位x86機器上輸出:

$ ./langxun.exe

-16

1

解析:在默認情況下,為了方便對結構體內元素的訪問和管理,當結構體內的元素長度都小於處理器的位數的時候,便以結構體里面最長的元素為對其單位,即結構體的長度一定是最長的數據元素的整數倍;如果有結構體內存長度大於處理器位數的元素,那么就以處理器的位數為對齊單元。由於是32位處理器,而且結構體中a和b元素類型均為int(也是4個字節),所以結構體的A占用內存為4個字節。

上例程序中定義了位域結構A,兩個個位域為a(占用5位),b(占用3位),所以a和b總共占用了結構A一個字節(低位的一個字節)。

當程序運行到14行時,d內存分配情況:

 

 高位 00110100 00110011   00110001    00110000 低位(ASCII碼)

       '4'       '3'       '1'          '0'  

 其中d.a和d.b占用d低位一個字節(00110000),d.a : 10000, d.b : 001

 d.a內存中二進制表示為10000,由於d.a為有符號的整型變量,輸出時要對符號位進行擴展,所以結果為-16(二進制為11111111111111111111111111110000)

 d.b內存中二進制表示為001,由於d.b為有符號的整型變量,輸出時要對符號位進行擴展,所以結果為1(二進制為00000000000000000000000000000001)

 

 (三)位域的對齊

  如果結構體中含有位域(bit-field),那么VC中准則是:

 

  1) 如果相鄰位域字段的類型相同,且其位寬之和小於類型的sizeof大小,則后面的字段將緊鄰前一個字段存儲,直到不能容納為止;

  2) 如果相鄰位域字段的類型相同,但其位寬之和大於類型的sizeof大小,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數倍;

  3) 如果相鄰的位域字段的類型不同,則各編譯器的具體實現有差異,VC6采取不壓縮方式(不同位域字段存放在不同的位域類型字節中),Dev-C++和GCC都采取壓縮方式;

 

  系統會先為結構體成員按照對齊方式分配空間和填塞(padding),然后對變量進行位域操作。

 

轉自:http://blog.csdn.net/u014186096/article/details/48290013#


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM