C/C++內存對齊詳解

一、什麼是內存對齊

仍是用一個例子帶出這個問題,看下面的小程序,理論上,32位系統下,int佔4byte,char佔一個byte,那麼將它們放到一個結構體中應該佔4+1=5byte;可是實際上,經過運行程序獲得的結果是8 byte,這就是內存對齊所致使的。linux

//32位系統
#include<stdio.h>
struct{
    int x;
    char y;
}s;

int main()
{
    printf("%d\n",sizeof(s);  // 輸出8
    return 0;
}

 

現代計算機中內存空間都是按照 byte 劃分的,從理論上講彷佛對任何類型的變量的訪問能夠從任何地址開始,可是實際的計算機系統對基本類型數據在內存中存放的位置有限制,它們會要求這些數據的首地址的值是某個數k(一般它爲4或8)的倍數,這就是所謂的內存對齊。小程序

二、爲何要進行內存對齊

儘管內存是以字節爲單位,可是大部分處理器並非按字節塊來存取內存的.它通常會以雙字節,四字節,8字節,16字節甚至32字節爲單位來存取內存,咱們將上述這些存取單位稱爲內存存取粒度.佈局

如今考慮4字節存取粒度的處理器取int類型變量(32位系統),該處理器只能從地址爲4的倍數的內存開始讀取數據。測試

假如沒有內存對齊機制,數據能夠任意存放,如今一個int變量存放在從地址1開始的連續四個字節地址中,該處理器去取數據時,要先從0地址開始讀取第一個4字節塊,剔除不想要的字節(0地址),而後從地址4開始讀取下一個4字節塊,一樣剔除不要的數據(5,6,7地址),最後留下的兩塊數據合併放入寄存器.這須要作不少工做.編碼

簡單的說內存對齊可以提升 cpu 讀取數據的速度,減小 cpu 訪問數據的出錯性(有些 cpu 必須內存對齊,不然指針訪問會出錯)spa

 

如今有了內存對齊的,int類型數據只能存放在按照對齊規則的內存中,好比說0地址開始的內存。那麼如今該處理器在取數據時一次性就能將數據讀出來了,並且不須要作額外的操做,提升了效率。3d

三、內存對齊規則

每一個特定平臺上的編譯器都有本身的默認「對齊係數」(也叫對齊模數)。gcc中默認#pragma pack(4),能夠經過預編譯命令#pragma pack(n),n = 1,2,4,8,16來改變這一系數。指針

有效對其值:是給定值#pragma pack(n)和結構體中最長數據類型長度中較小的那個。有效對齊值也叫對齊單位。code

瞭解了上面的概念後,咱們如今能夠來看看內存對齊須要遵循的規則:blog

(1) 結構體第一個成員的偏移量(offset)爲0,之後每一個成員相對於結構體首地址的 offset 都是該成員大小與有效對齊值中較小那個的整數倍,若有須要編譯器會在成員之間加上填充字節。

(3) 結構體的總大小爲 有效對齊值 的整數倍,若有須要編譯器會在最末一個成員以後加上填充字節。

下面給出幾個例子以便於理解:

//32位系統
#include<stdio.h>
struct
{
    int i;    
    char c1;  
    char c2;  
}x1;

struct{
    char c1;  
    int i;    
    char c2;  
}x2;

struct{
    char c1;  
    char c2; 
    int i;    
}x3;

int main()
{
    printf("%d\n",sizeof(x1));  // 輸出8
    printf("%d\n",sizeof(x2));  // 輸出12
    printf("%d\n",sizeof(x3));  // 輸出8
    return 0;
}

 

以上測試都是在Linux環境下進行的,linux下默認#pragma pack(4),且結構體中最長的數據類型爲4個字節,因此有效對齊單位爲4字節,下面根據上面所說的規則以s2來分析其內存佈局:

首先使用規則1,對成員變量進行對齊:

sizeof(c1) = 1 <= 4(有效對齊位),按照1字節對齊,佔用第0單元;

sizeof(i) = 4 <= 4(有效對齊位),相對於結構體首地址的偏移要爲4的倍數,佔用第4,5,6,7單元;

sizeof(c2) = 1 <= 4(有效對齊位),相對於結構體首地址的偏移要爲1的倍數,佔用第8單元;

而後使用規則2,對結構體總體進行對齊:

s2中變量i佔用內存最大佔4字節,而有效對齊單位也爲4字節,二者較小值就是4字節。所以總體也是按照4字節對齊。由規則1獲得s2佔9個字節,此處再按照規則2進行總體的4字節對齊,因此整個結構體佔用12個字節。

根據上面的分析,不可貴出上面例子三個結構體的內存佈局以下:

#pragma pack(n)

不一樣平臺上編譯器的 pragma pack 默認值不一樣。而咱們能夠經過預編譯命令#pragma pack(n), n= 1,2,4,8,16來改變對齊係數。

例如,對於上個例子的三個結構體,若是前面加上#pragma pack(1),那麼此時有效對齊值爲1字節,此時根據對齊規則,不難看出成員是連續存放的,三個結構體的大小都是6字節。

若是前面加上#pragma pack(2),有效對齊值爲2字節,此時根據對齊規則,三個結構體的大小應爲6,8,6。內存分佈圖以下:

通過上面的實例分析,你們應該對內存對齊有了全面的認識和了解,在之後的編碼中定義結構體時須要考慮成員變量定義的前後順序了。

參考資料:
http://light3moon.com/2015/01/19/[%E8%BD%AC]%20%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90/

相關文章
相關標籤/搜索