徹底搞清計算結構體大小和數據對齊原則



           徹底搞清計算結構體大小和數據對齊原則

                                              By Qianghaohao
                       
           數據對齊:
                     許多計算機系統對基本數據類型合法地址做出了一些限制,要求某種類型對象的地址必須是
          某個值K(通常是2,4或8)的倍數。這種對齊限制簡化了形成處理器和存儲器系統之間的接口的硬件
          設計。例如,假設一個處理器總是從存儲器中取出8個字節,則地址必須為8的倍數。如果我們能保
          證將所有的double類型數據的地址對齊成8的倍數,那么就可以用一個存儲器操作來讀或者寫值了。
         否則,我們可能需要執行兩次存儲器訪問,因為對象可能被分放在兩個8字節存儲塊中。
          
          當數據類型為結構體時,編譯器可能需要在結構體字段的分配中插入間隙,以保證每個結構元素都
         滿足它的對齊要求。而結構本身對它的地址也有一些對齊要求,此時可能需要在結構末尾填充一些
         空間,以滿足結構體整體的對齊----向結構體元素中最大的元素對齊。稍后會用代碼說明!!!
       
         Linux和Microsoft Windows的對齊方式有何不同:
               一.Linux的對齊策略:
                       在Linux中2字節數據類型(例如short)的地址必須是2的倍數,而較大的數據類型(例如int,int *
               ,float和double)的地址必須是4的倍數。也就是說Linux下要么2字節對齊,要么4字節對齊,沒
               有其他格式的對齊。
              二.Microsoft Windows的對齊策略:
                       在Windows中對齊要求更嚴--任何K字節基本對象的地址都必須是K的倍數,K=2,4,或者8.
            特別地,double或者long long類型數據的地址應該是8的倍數。可以看出Windows的對齊策略和
            Linux還是不同的。稍后用代碼說明!!!
          
           接下來用代碼和圖文說明兩者的對齊方式(不同的對齊方式產生的結構體大小不同):
               測試代碼如下:
              
/////////////////////////////////////
//   filename:DataAlignment
/////////////////////////////////////
#include<stdio.h>

typedef struct 
{
	char c;
	int i[2];
	double v;
}S;

int main()
{
	printf("size of S = %d\n", sizeof(S));
	return 0;
}
         程序中定義了一個結構體,在沒有任何數據對齊時內存布局如下:
        
          一.在紅帽Linux i686上編譯編譯后結構S的布局如下:
                                 
           由於要保證結構體每個元素都要數據對齊,因此必須在c和i[0]之間插入3字節的間隙(圖中陰影部分為編譯器插入的間隙)
           使得i[0]和后面的元素的的偏移量都為4的倍數,這樣最終S結構大小為20字節。
                       運行程序結果為:
                           size of S = 20
           
           二.在Microsoft Windows 上編譯后S的內存布局如下:
                
                       
         在windows下int類型4個字節,因此int類型要向4字節對齊,double類型8字節,因此要向8字節對齊。因此在
         c和i[0]之間插入3字節的間隙(圖中陰影部分),使得i[0]的偏移量為4的倍數,同時在i[1]和v之間插入4字節的間隙,
        使得v的偏移量為8的倍數。這樣最終S結構的大小為24字節。
                       運行程序結果為:
                           size of S = 24
          從以上對比可以看出Linux下和windows下的對齊策略是不同的,這就導致在兩個平台下結構體的大小不同。
           
         現在考慮如下代碼:
              
#include<stdio.h>
typedef struct
{
    int i;
    int j;
    char c;
}S1;
int main()
{
   printf("size of S1 = %d\n", sizeof(S1));
   return 0;
}
       可能很多人認為編譯后運行結果為9,以為結構體的每個元素都滿足各自的對齊要求。其實不然,別忘了還有要考慮
       結構體整體的對齊。假如有如下聲明:
              S1  d[4];
       如果這樣分配9個字節,不可能滿足d的每個元素的對齊要求,因為這些元素的地址分別為xd,xd+9,xd+18,
       xd+27。這樣只有第一個元素滿足4字節對齊的要求,而其他的元素的地址都不是4的倍數。

      因此編譯器會在結構體的末尾填充3字節滿足結構體整體的對齊要求,填充后的內存布局如下:
       
                      這樣一來,d的元素的地址分別為xd,xd+12,xd+24,xd+36,滿足4字節的整數倍,這樣最終S1的大小
                      為12字節,而不是9字節.

                   總結:通過代碼的對比,可以看出Linx和Windwos的數據對齊有所差異,這就導致
        在一些情況下兩者平台結構體類型大小的不同。通過以上示例分析我們可以很簡單的
        計算出在任何平台下結構體的大小。讀者可以找相關的練習題繼續練習下,驗證結果
        的正確性。相信仔細閱讀本文應該能搞定所有類似的問題!!!

                本文參考書籍:
                      鏈接:   深入理解計算機系統原書第2版
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM