天天看點

sizeof()用法和記憶體對齊分析

sizeof()功能:計算資料空間的位元組數

1.與strlen()比較

       strlen()計算字元數組的字元數,以"/0"為結束判斷,不計算為'/0'的數組元素。

       而sizeof計算資料(包括數組、變量、類型、結構體等)所占記憶體空間,用位元組數表示。

2.指針與靜态數組的sizeof操作

      指針均可看為變量類型的一種。所有指針變量的sizeof 操作結果均為4。

      注意:int *p; sizeof(p)=4;

                    但sizeof(*p)相當于sizeof(int);    

      對于靜态數組,sizeof可直接計算數組大小;

      例:int a[10];char b[]="hello";

            sizeof(a)等于4*10=40;

            sizeof(b)等于6;

      注意:數組做型參時,數組名稱當作指針使用!!

                void fun(char p[])

                {

                       sizeof(p)   // 等于4

                }  

經典問題:

       double* (*a)[3][6];

       cout<<sizeof(a)<<endl; // 4 a為指針

       cout<<sizeof(*a)<<endl; // 72 *a為一個有3*6個指針元素的數組

       cout<<sizeof(**a)<<endl; // 24 **a為數組一維的6個指針

       cout<<sizeof(***a)<<endl; // 4 ***a為一維的第一個指針

       cout<<sizeof(****a)<<endl; // 8 ****a為一個double變量

問題解析:

      a是一個很奇怪的定義, 他表示一個指向double*[3][6]類型數組的指針。  既然是指針, 是以sizeof(a)就是4。  既然a是執行double*[3][6]類型的指針, *a就表示一個double*[3][6]的多元數組類型, 是以sizeof(*a) = 3*6*sizeof(double*) = 72。 同樣的, **a表示一個double*[6]類型的數組, 是以sizeof(**a) = 6*sizeof (double*) = 24。 ***a就表示其中的一個元素,也就是double*了,是以sizeof(***a) = 4。至于 ****a,就是一個double了,是以sizeof(****a) = sizeof(double)=8。

3.格式的寫法

       sizeof操作符,對變量或對象可以不加括号,但若是類型,須加括号。

4.使用sizeof時string的注意事項

       string s="hello";

       sizeof(s)等于string類的大小,sizeof(s.c_str())得到的是與字元串長度。

5.union 與struct的空間計算

      總體上遵循兩個原則:

      (1)整體空間是 占用空間最大的成員(的類型)所占位元組數的整倍數

      (2)資料對齊原則----記憶體按結構成員的先後順序排列,當排到該成員變量時,其前面已擺放的空間大小必須是該成員類型大小的整倍數,如果不夠則補齊,以此向後類推。。。。。

      注意:數組按照單個變量一個一個的擺放,而不是看成整體。如果成員中有自定義的類、結構體,也要注意數組問題。

例:[引用其他文章的内容]

因為對齊問題使結構體的sizeof變得比較複雜,看下面的例子:(預設對齊方式下)

struct s1

{

char a;

double b;

int c;

char d;

};

struct s2

{

char a;

char b;

int c;

double d;

};

cout<<sizeof(s1)<<endl; // 24

cout<<sizeof(s2)<<endl; // 16

       同樣是兩個char類型,一個int類型,一個double類型,但是因為對齊問題,導緻他們的大小不同。計算結構體大小可以采用元素擺放法,我舉例子說明一下:首先,CPU判斷結構體的對界,根據上一節的結論,s1和s2的對界都取最大的元素類型,也就是double類型的對界8。然後開始擺放每個元素。

      對于s1,首先把a放到8的對界,假定是0,此時下一個空閑的位址是1,但是下一個元素d是double類型,要放到8的對界上,離1最接近的位址是8了,是以d被放在了8,此時下一個空閑位址變成了16,下一個元素c的對界是4,16可以滿足,是以c放在了16,此時下一個空閑位址變成了20,下一個元素d需要對界1,也正好落在對界上,是以d放在了20,結構體在位址21處結束。由于s1的大小需要是8的倍數,是以21-23的空間被保留,s1的大小變成了24。

      對于s2,首先把a放到8的對界,假定是0,此時下一個空閑位址是1,下一個元素的對界也是1,是以b擺放在1,下一個空閑位址變成了2;下一個元素c的對界是4,是以取離2最近的位址4擺放c,下一個空閑位址變成了8,下一個元素d的對界是8,是以d擺放在8,所有元素擺放完畢,結構體在15處結束,占用總空間為16,正好是8的倍數。

     這裡有個陷阱,對于結構體中的結構體成員,不要認為它的對齊方式就是他的大小,看下面的例子:

struct s1

{

char a[8];

};

struct s2

{

double d;

};

struct s3

{

s1 s;

char a;

};

struct s4

{

s2 s;

char a;

};

cout<<sizeof(s1)<<endl; // 8

cout<<sizeof(s2)<<endl; // 8

cout<<sizeof(s3)<<endl; // 9

cout<<sizeof(s4)<<endl; // 16;

       s1和s2大小雖然都是8,但是s1的對齊方式是1,s2是8(double),是以在s3和s4中才有這樣的差異。

       是以,在自己定義結構體的時候,如果空間緊張的話,最好考慮對齊因素來排列結構體裡的元素。

補充:不要讓double幹擾你的位域

  在結構體和類中,可以使用位域來規定某個成員所能占用的空間,是以使用位域能在一定程度上節省結構體占用的空間。不過考慮下面的代碼:

struct s1

{

 int i: 8;

 int j: 4;

 double b;

 int a:3;

};

struct s2

{

 int i;

 int j;

 double b;

 int a;

};

struct s3

{

 int i;

 int j;

 int a;

 double b;

};

struct s4

{

 int i: 8;

 int j: 4;

 int a:3;

 double b;

};

cout<<sizeof(s1)<<endl; // 24

cout<<sizeof(s2)<<endl; // 24

cout<<sizeof(s3)<<endl; // 24

cout<<sizeof(s4)<<endl; // 16

  可以看到,有double存在會幹涉到位域(sizeof的算法參考上一節),是以使用位域的的時候,最好把float類型和double類型放在程式的開始或者最後。

相關資料類型位元組數:

sizeof int:4

sizeof short:2

sizeof long:4

sizeof float:4

sizeof double:8

sizeof char:1

sizeof p:4

sizeof WORD:2

sizeof DWORD:4

繼續閱讀