内存对齐规则总结
由于某些硬件平臺不能任意訪問地址數據,只能在某些地址處取某些特定類型的數據;并且處理器訪問未對齊的內存時,需要多次讀取并對多余數據進行剔除,相較于對齊內存訪問,耗費了更多的時間,降低了數據訪問效率,因此需要內存對齊。
一、內存字節對齊的規則
1.數據類型自然邊界對齊
char型數據自身對齊值為1字節,short型數據為2字節,int/float型為4字節,double型為8字節,long型數據為4字節(32位編譯器)或8字節(64位編譯器),void*型數據為4字節(32位編譯器)或8字節(64位編譯器)。
2.結構體、類的自身對齊
為結構體分配內存時,分配的內存大小至少是各個字段的長度和。通常,分配的結構體的長度會大于結構體各個字段的長度和,因為結構體需要對齊,即結構體各字段之間需要填充。
缺省情況下,編譯器為結構體的每個成員按其自然邊界對齊方式分配空間,并按照每個成員被聲明的順序在內存中順序存儲,第一個成員的地址和整個結構體的地址相同。結構體整體的默認字節對齊值是結構體中所有成員中對齊參數最大值,結構體長度的計算必須取所用過的所有對齊參數的整數倍。
結構體中每個成員的首地址是成員自身自然邊界對齊值的整數倍,如果成員自身大小大于指定對齊字節,以指定對齊字節整數倍為基準對齊;
結構體整體對齊方式取決于結構體中所有成員的自然邊界對齊值最大值的整數倍,如果最大成員大小超過指定對齊字節,以指定對齊字節整數倍為基準對齊(也叫補齊,目的是多個結構體連續存儲時也滿足對齊要求)。
3.編譯器指定對齊
內存字節對齊是GCC編譯器對C語言進行的擴展。在缺省情況下,C編譯器為每一個變量或是數據單元按其自然邊界對齊條件分配空間。
(1)GCC編譯器兩種內存對齊的方法
屬性設置方式(推薦)
__attribute__ ((aligned(n))); //讓所作用的結構體或者類或者聯合或者一個類型的變量(對象)分配地址空間時的地址在編譯過程中按n字節對齊
__attribute__ ((packed)); //取消結構在編譯過程中的優化對齊
如果__attribute__((aligned(n)))作用于一個類型,那么該類型的變量在分配地址空間時,其存放的地址一定按照n字節對齊(n必須是2的冪次方);如果類型中的成員的自然邊界對齊值大于n,則按照機器字長(32位或者64位)對齊。類型占用的空間,即大小,需要是n的整數倍,以保證在申請連續存儲空間的時候,每一個元素的地址也是按照n字節對齊。
32位處理器指定4字節對齊方式實例如下:
struct test{char a[18];double b;char c;int d;short e;
}__attribute__((aligned(4)));//實際有效指定對齊值為4字節(機器字長)sizeof(struct test);//40字節
第一個成員(char a[18]):假設放在內存開始地址為0的位置,占用內存地址范圍為0~18。
第二個成員(double b):double類型占8字節,大于指定對齊字節(4字節),因此以4字節對齊為基準。由于第一個成員結束地址為18,不是4的整數倍,需要再加2個字節從地址20開始。
第三個成員(char c):char類型占1字節,可以直接從第二個成員結束地址28開始。
第四個成員(int d):int類型占4字節,地址29不是4的整數倍,需要加3個字節,從地址32開始。
第五個成員(short e):short類型占2字節,地址36剛好是2的整數倍,可以直接放在當前地址。
最后是對整個結構體補齊,該結構體中最大字節為8字節(double類型),大于指定對齊字節,所以還是以4字節補齊為基準。整個結構體結束地址為38,地址38不是4的整數倍,需要額外加2個字節填充,即整個結構體的大小為40字節。具體對齊結構如下圖所示。
偽指令方式(最多只支持8字節對齊,不建議使用)
#pragma pack(n) //n取值可以為1、2、4、8,在編譯過程中按照n個字節對齊
#pragma pack() //取消指定對齊,按照編譯器的優化對齊方式對齊
32位處理器指定4字節對齊方式實例如下:
#pragma pack(4)
struct test{char a;int b;short c;char *p;double d;
};
#pragma pack()sizeof(struct test);//24字節
(2)其他編譯器內存對齊方式
#if defined (__CC_ARM) /*!< ARM Compiler */ //MDK__align(4) uint16_t data[40];//存儲類修飾符,只修飾最高級類型對象,不能用于結構或者函數對象//__packed是1字節對齊,不使用__packed的話系統以默認方式對齊#elif defined ( __ICCARM__ ) /*!< IAR Compiler */ #pragma data_alignment=4 uint16_t data[40];#elif defined (__GNUC__) /*!< GNU Compiler */ __attribute__ ((aligned (4))) uint16_t data[40]; #elif defined (__TASKING__) /*!< TASKING Compiler */ __align(4) uint16_t data[40];#endif /* __CC_ARM */
二、檢查內存地址是否對齊(4字節為例)
4字節對齊的地址是4(100b)字節的整數倍,也就是說地址的二進制表示形式以00結尾,因此可以通過以下方式對4字節對齊地址進行測試:
if((address & 0x3) == 0)
{//The address is 4-byte aligned here
}if(address & 0x3)
{//The address is not 4-byte aligned here
}
總結
- 上一篇: Java中的List你真的会用吗
- 下一篇: build-helper-maven-p