2013年10月13日 星期日

sizeof( struct ) 的其中奧妙

sizeof(結構體型別) 就等於 size(成員變數) 的總和嗎?請先見下面的例子 StructSamples.cpp,兩個以 struct 定義出的自訂型別的資料成員都相同,但是順序不一樣:


#include <stdio.h>

struct TYPEA
{   short int code;
    float value;
    char flags;
};

struct TYPEB     // TYPEA 與 TYPEB 只有成員順序不同
{   float value;
    short int code;
    char flags;
};

void main(void)
{
    printf("sizeof(TYPEA) = %d\n", sizeof(TYPEA));
    printf("sizeof(TYPEB) = %d\n", sizeof(TYPEB));
}

上例的執行結果是:
sizeof(TYPEA) = 12
sizeof(TYPRB) = 8
為甚麼會不一樣?好像都是 4 的倍數?其實這是因為編譯器對 struct 作了最佳化,以現行 32-bits 的 CPU (或 32-bits 的應用程式)而言,CPU 的行為一次抓取就是 4 bytes,以下圖最左邊的狀況,假如編譯器未對 TYPEA 做最佳化,那麼它的 value 欄位剛好跨在 CPU 兩次抓取範圍之間,所以程式碼中要取用 value 欄位的時候,CPU 必須抓取兩次,然後取第一次抓取的後半部和第二次抓取的前半部才能或得 value 的資料內容,相當傷害效率,所以通常編譯器在安排 TYPEA 的記憶體布局(layout)的時候,會採用下圖中的設計,盡量保證 struct 自訂型別中的任何欄位 都可以一次抓取(除非像 double 或 64-bit 整數本身就已經 8 bytes 長),方法就是盡量讓資料欄位的位址落在 4 bytes 的記憶體邊界(boundary)上,甚至把整個結構體的尺寸填空到 4 的倍數,令後續的資料也會落在 4 bytes 倍數的位址上。



當然上圖中的 TYPEA 造成了記憶體的浪費,所以,好的 struct 設計應該主動地把資料欄位都放置 4 的倍數上,如 TYPEB 型別,雖然欄位和 TYPEA 相同,但是變個順序,就兼顧了效率和記憶體空間的要求了。除此之外,請記得struct 的尺寸不一定等於成員的尺寸和,所以計算配置記憶體所需的記憶體空間請務必使用 sizeof( )。


沒有留言:

張貼留言