2015年1月23日 星期五

[C/C++] 存取記憶體所需瞭解的對齊問題

寫程式對記憶體存取時,需要瞭解兩件事:1. Little Endian與Big Endian在記憶體裡存放的差別;2.  記憶體資料的對齊問題。此篇文章所要探討的即是對齊問題,以現有的計算機(computer)來說,許多計算機均有能力去存取對齊和不對齊在記憶體位址上的資料,計算機結構-計量方法這本書就解釋得非常清楚,現有一個大小為N的資料存放在某一記憶體位址
A上,只要此位址A除以N且可以被整除的話,就代表此資料是對齊的(aligned):反之就是不對齊的(misaligned),以計算機結構裡的圖可以讓人簡單明瞭 (註:從白算盤到計算機結構主要都是以MIPS架構作為主題,書中的word代表32位元,但是在x86/x64架構下word則是代表16位元):

Fig.1 Aligned and misaligned address.
由於計算機是以位元組定址,所以byte存放在任何位址上均是對齊在byte boundary上,但若是half word, word或double world的話,就會有對齊和不對齊的問題,若要能夠存取非對齊的資料,計算機的設計也會變得比較複雜。既然有些計算機均可存取對齊和不對齊的資料,那對不對齊又有何差別? 答案當然是有差別的,以不對齊的資料來說,計算機會存取的比已經對齊好的資料還要慢,所以編譯器通常會將程式碼的資料做對齊處理,為了讓人有更深入的感受,在此宣告一結構說明:
struct AlignedStruct{
    uint8_t         uint8Var1; 
    uint64_t        uint64Var2;
    uint8_t         uint8Var3; 
    uint16_t        uint16Var4;
    uint8_t         uint8Var5;
    uint32_t        uint32Var6;
    uint64_t        uint64Var7;
    uint32_t        uint32Var8;
};

uint64_t getAlignedAddress(uint64_t currentElementAddr,uint16_t currentElementSize,uint16_t nextElementSize){
    uint64_t address = currentElementAddr + currentElementSize;

    for(;;){
        if((address%nextElementSize) == 0)
            break;
        else
            address ++;
    }
    return address;
}

getAlignedAddress()是根據上述說明,用來計算下一筆資料的對齊位址,第二行算出下一筆資料的起始位址,由於無法肯定此位址是不是對齊好的位址,所以第四到第九行用來計算下一筆資料的實際對齊位址。因此AlignedStruct結構在記憶體裡的排放方式如Fig. 2所顯示的樣子:
Fig.2 Struct layout in the memory space.
目前為止都是根據理論來說明,下例的程式碼用來確認實際上的狀況:

待補...

沒有留言:

張貼留言