一、為什么要內存對齊
Arm對內存的訪問支持字(4byte)、半字(2byte)、字節(1byte)的直接訪問,但是呢他們是有一定的要求的:
-
存取字時要求地址按字對齊,也就是地址要是4的整數倍,如0x0000、0x0004、0x0008(該地址只是舉例,mcu的地址分配請參考具體手冊的地址映射圖)
-
存取半字是要求地址按半字對齊,也就是地址是2的倍數,這樣假如通過0x0001、0x0003這樣非2倍數的地址來讀取一個半字就會進入硬件中斷錯誤
-
存取字節簡單,只要地址不超范圍就可以
二、內存對齊的意義是什么??
? ? ? ? 提高內存訪問速度
????????盡管內存是以字節為單位,但是大部分處理器并不是按字節塊來存取內存的.它一般會以雙字節,四字節,8字節,16字節甚至32字節為單位來存取內存,我們將上述這些存取單位稱為內存存取粒度.
現在考慮4字節存取粒度的處理器取int類型變量(32位系統),該處理器只能從地址為4的倍數的內存開始讀取數據。
假如沒有內存對齊機制,數據可以任意存放,現在一個int變量存放在從地址1開始的聯系四個字節地址中,該處理器去取數據時,要先從0地址開始讀取第一個4字節塊,剔除不想要的字節(0地址),然后從地址4開始讀取下一個4字節塊,同樣剔除不要的數據(5,6,7地址),最后留下的兩塊數據合并放入寄存器.這需要做很多工作。但如果有內存對齊機制,那么直接從0地址讀出4個字節,就可以得到數據內容。
三、內存對齊規則
- 數據成員對齊:成員根據其自身大小,從自身大小的整數倍內存地址(以第一個元素存儲在0位置為參考)開始存儲;
- 結構體成員對齊:首個成員從偏移量?
0
?開始存儲。后續成員偏移地址為?min(自身對齊值, 編譯器指定對齊值)
?的整數倍。 - 結構體總大小對齊:結構體總大小需為?最大成員對齊值?的整數倍,不足時末尾填充字節。
四、如何實現內存對齊
#pragma pack(n)
? ?作用:強制指定對齊值為?n
#pragma pack(1) // 設置為1字節對齊(無填充)
struct Data {char a; // 1字節 int b; // 4字節(緊密排列)
}; // sizeof = 5
#pragma pack() // 恢復默認對齊
__attribute__((aligned(n)))
? ? 作用:指定結構體/變量的最小對齊值。
struct __attribute__((aligned(8))) AlignStruct {char c; // 結構體整體按8字節對齊 int i;
}; // sizeof = 8(而非5)
注意:主要方式跨平臺,由于不同平臺編譯器默認的對齊大小不同,導致硬件中斷問題,例如Windows默認8字節對齊,Linux默認4字節
五、字節對齊應用場景
比如別人傳輸一大段數據過來,然后它默認一字節對齊,此時數據接收就會紊亂,這時候就必須強制對齊
typedef union __attribute__((aligned(16))) DataPacket { uint16_t raw_bytes[10]; // 原始字節訪問接口 struct SensorData { uint32_t temperature; // 溫度值 (4字節)uint8_t status; // 狀態標志 (1字節)uint16_t id; // 設備ID (2字節)uint8_t status2; // 狀態標志 (1字節)uint8_t data[3];uint8_t status3; // 狀態標志 (1字節)} sensor;
} DataPacket;