天天看點

C++入門——sizeof與位元組對齊

sizeof

strlen("\0")與sizeof("\0")

strlen("\0") = 0

sizeof("\0") = 2

strlen

strlen用來計算字元串的長度,它從記憶體的某個位置開始掃描,直到碰到第一個字元串結束符’\0’為止,然後傳回計數器值

sizeof

sizeof是C語言的關鍵字,它以位元組的形式給出了其操作數的存儲大小,操作數可以是一個表達式或括在括号内的類型名,操作數的存儲大小由操作數的類型決定

strlen與sizeof的差別

  1. sizeof是運算符(sizeof既是關鍵字,也是運算符,但不是函數),而strlen是函數。sizeof後如果是類型,則必須加括弧,如果是變量名,則可以不加括弧
  2. sizeof運算符的結果類型是size_t,它在頭檔案中typedef為unsigned int類型。該類型保證能夠容納實作所建立的最大對象的位元組大小
  3. sizeof可以用類型作為參數,strlen隻能用char*作為參數,而且必須是以’\0’結尾的。sizeof還可以以函數作為參數,如

    int g();

    ,則

    sizeof(g())

    的值等于sizeof(int)的值,在32位計算機下,該值為4
  4. 大部分編譯程式的sizeof都是在編譯的時候計算的,是以可以通過

    sizeof(x)

    來定義數組維數。而strlen則是在運作期計算的,用來計算字元串的實際長度,不是類型占記憶體的大小。

對于結構體,為什麼sizeof傳回的值一般大于期望值

struct是一種複合資料類型,其構成元素既可以是基本資料類型,如int、double、float、short、char等,也可以是複合資料類型,如數組、struct、union等資料單元

一般而言,struct的sizeof是所有成員對齊後長度相加,而union的sizeof是取最大的成員長度

位元組對齊

位元組對齊也稱為位元組填充,它是C++編譯器的一種技術手段,主要是為了在空間與複雜度上達到平衡。簡單地講,是為了在可接受的空間浪費的前提下,盡可能地提高對相同運算過程的最少(快)處理。位元組對齊的作用不僅是便于CPU的快速通路,使CPU的性能達到最佳,而且可以有效地節省存儲空間。

例如,32位計算機的資料傳輸是4位元組,64位計算機的資料傳輸是8位元組,這樣,struct在預設情況下,編譯器會對struct的結構進行(32位機)4的倍數或(64位機)8的倍數的資料對齊。對于32位機來說,4位元組的對齊能夠使CPU通路速度提高,如一個long類型的變量,如果跨越了4位元組邊界存儲,那麼CPU要讀取兩次,這樣效率就低了。但需要注意的是,如果在32位機中使用1位元組或者2位元組對齊,不僅不會提高效率,反而會使通路速度降低。

修改對齊條件

在預設情況下,編譯器為每一個變量或資料單元按其自然對齊條件配置設定空間。但是可以通過下面的方法來改變預設的對齊條件

  1. 使用僞指令

    #pragma pack(n)

    ,編譯器将按照n個位元組對齊
  2. 使用僞指令

    #pragma pack()

    ,取消自定義位元組對齊方式
  3. _attribute((aligned(n))

    ,讓所作用的結構成員對齊在n位元組自然邊界上。如果結構中有成員的長度大于n,則按照最大成員的長度來對齊
  4. _attribute((packed))

    ,取消結構在編譯過程中的優化對齊,按照實際占用位元組數進行對齊

位元組對齊的準則

位元組對齊的細節和編譯器實作相關,但一般而言,滿足以下準則:

  1. 結構體變量的首位址能夠被其最寬基本類型成員的大小所整除
  2. 結構體每個成員相對于結構體首位址的偏移量(offset)都是成員大小的整數倍。如有需要,編譯器會在成員之間加上填充位元組
  3. 結構體的總大小為結構體最寬基本類型成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充位元組

其中的基本類型即char、short、int、float、double這樣的内置資料類型,這裡說的“資料寬度”就是指其sizeof的大小

由于結構體的成員可以是複合類型,如果一個結構體中包含另外一個結構體成員,那麼此時最寬基本成員不是該結構體成員,而是取其基本類型的最寬值。但在确定複合類型成員的偏移位置時,則是将複合類型作為整體看待,即複雜類型(如結構)的預設對齊方式是它最長的成員的對齊方式,這樣在成員是複雜類型時,可以最小化長度,達到程式優化的目的

指針進行強制類型轉換後與位址進行加法運算

假設在32位機器上,在對齊為4情況下,

sizeof(long)

的結果為4位元組,

sizeof(char*)

的結果為4位元組,

sizeof(short int)

的結果與

sizeof(short)

的結果都為2位元組,

sizeof(char)

的結果為1位元組,

sizeof(int)

的結果為4位元組,由于32位機器上是4位元組對齊,以如下結構體為例:

struct A
{
    long num;
    char *name;
    short int data;
    char ha;
    short ba[5];
}*p;
           

在32位機器下,

sizeof(struct A) = 4 + 4 + 2 + 2 + 1 + 3 + 2*5 + 2 = 24

位元組

p=0x100000

,那麼

p + 0x200 = 0x1000000 + 0x200*24

指針加法,加出來的是指針所指類型的位元組長度的整數倍,就是p偏移

sizeof(*p)*0x200

(ulong)p + 0x200 = 0x1000000 + 0x200

經過

ulong

後,已經不再是指針加法,而變成一個數值加法了

另外

(char*)p + 0x200 = 0x1000000 + 0x200*sizeof(char)

結果類型是

char*

繼續閱讀