sizeof與記憶體對齊

來源:互聯網
上載者:User

sizeof 乃 C/C++ 中的一個操作符(operator)是也。簡單說其作用就是返回一個對象或者類型所佔的記憶體位元組數,傳回值類型為size_t

 

文法:
sizeof有三種文法形式,如下:
1) sizeof( object );    // sizeof( 對象 );
2) sizeof( type_name ); // sizeof( 類型 );

3) sizeof object;       // sizeof 對象;

所以:

int i;
sizeof( i );     // ok
sizeof i;        // ok
sizeof( int );   // ok
sizeof int;      // error

 

sizeof可以對一個運算式求值。編譯器根據運算式的最終結果類型來確定大小,一般不會對錶達式進行計算。

char fun()<br />{<br />cout<<"fun()"<<endl;<br />return 'c';<br />}</p><p>void main()<br />{<br />cout<<sizeof(fun())<<endl;//輸出1,fun()返回char型,但是並未列印"fun()",即fun未執行<br />cout<<sizeof(2)<<endl;//輸出4,2為整形<br />cout<<sizeof(2.0+3)<<endl;//輸出8,2.0為double,3被提升至整形<br />cout<<sizeof("Grubby")<<endl;//輸出7,字串"Grubby"並沒有被認為是const char*<br />}

C99標準規定,函數、不能確定類型的運算式以及位域(bit-field)成員不能被計算sizeof值,即下面這些寫法都是錯誤的:

sizeof(void);

sizeof( foo );

void foo2() { }
sizeof( foo2() );

struct S
{
     unsigned int f1 : 1;
     unsigned int f2 : 5;
     unsigned int f3 : 12;
};
sizeof( S.f1 );

 

電腦群組成原理教導我們,這樣有助於加快電腦的取數速度,否則就得多花指令周期了。

為此,編譯器預設會對結構體進行處理(實際上其它地方的資料變數也是如此),讓寬度為2的基礎資料型別 (Elementary Data Type)(short等)都位於能被2整除的地址上,讓寬度為4的基礎資料型別 (Elementary Data Type)(int等)都位於能被4整除的地址上。以此類推,這樣,兩個數中間就可能需要加入填充位元組,所以整個結構體的sizeof值就增長了。

位元組對齊的細節和編譯器實現相關,但一般而言,滿足三個準則:
    1) 結構體變數的首地址能夠被其最寬基本類型成員的大小所整除;
    2) 結構體每個成員相對於結構體首地址的位移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組(internal adding);
    3) 結構體的總大小為結構體最寬基本類型成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充位元組(trailing padding)。

 

基本類型是指前面提到的像char、short、int、float、double這樣的內建資料類型。這裡所說的“資料寬度”就是指其sizeof的大小。由於結構體的成員可以是複合類型,比如另外一個結構體,所以在尋找最寬基本類型成員時,應當包括複合類型成員的子成員,而不是把複合成員看成是一個整體。但在確定複合類型成員的位移位置時則是將複合類型作為整體看待。

舉個例子

struct S1<br />{<br />int i;<br />char c;<br />};<br />struct S2<br />{<br />char c1;<br />S1 s;<br />char c2;<br />};<br />void main()<br />{<br />cout<<sizeof(S1)<<endl;<br />cout<<sizeof(S2)<<endl;<br />}

S1的最寬簡單成員的類型為int,S3在考慮最寬簡單類型成員時是將S1“打散”看的,所以S3的最寬簡單類型為int。這樣,通過S3定義的變數,其儲存空間首地址需要被4整除,整個sizeof(S3)的值也應該被4整除。

c1的位移量為0,s的位移量呢?這時s是一個整體,它作為結構體變數也滿足前面三個準則,所以其大小為8,位移量為4,c1與s之間便需要3個填充位元組,而c2與s之間就不需要了,所以c2的位移量為12,算上c2的大小為13,13是不能被4整除的,這樣末尾還得補上3個填充位元組。最後得到sizeof(S3)的值為16。

 

#pragma pack(4)
class TestC
{
public:
  char a;
  short b;
  char c;
};
int nSize = sizeof(TestC);
按照正常的填充方式nSize的結果應該是8,為什麼結果顯示nSize為6呢?

事實上,很多人對#pragma pack的理解是錯誤的。
#pragma pack規定的對齊長度,實際使用的規則是:
結構,聯合,或者類的資料成員,第一個放在位移為0的地方,以後每個資料成員的對齊,按照#pragma pack指定的數值和這個資料成員自身長度中,比較小的那個進行。也就是說,當#pragma pack的值等於或超過所有資料成員長度的時候,這個值的大小將不產生任何效果。
而結構整體的對齊,則按照結構體中最大的資料成員 和 #pragma pack指定值 之間,較小的那個進行。

#pragma pack(4)
class TestC
{
public:
  char a;//第一個成員,放在[0]位移的位置,
  short b;//第二個成員,自身長2,#pragma pack(4),取2,按2位元組對齊,所以放在位移[2,3]的位置。
  char c;//第三個,自身長為1,放在[4]的位置。
};
整個類的大小是5位元組,按照min(sizeof(short),4)位元組對齊,也就是2位元組對齊,結果是6
所以sizeof(TestC)是6。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.