內存對齊是為了提高cpu訪問效率并滿足硬件要求。1. 數據類型需按自身大小對齊,如int按4字節對齊;2. 結構體成員起始地址必須是其類型對齊值的整數倍,否則插入填充字節;3. 結構體整體大小需為最大成員對齊值的整數倍;4. 成員順序影響填充量,合理排序可減少空間浪費;5. alignas關鍵字可顯式控制對齊方式,適用于底層優化場景。
理解c++的內存對齊規則,其實核心在于搞清楚兩個問題:為什么需要對齊和怎么對齊。簡單來說,CPU在讀取內存時,訪問特定類型的數據如果落在它要求的對齊地址上,效率更高,甚至有些平臺強制要求必須對齊,否則會拋異常或者性能下降明顯。
所以結構體填充(padding)和alignas關鍵字,都是圍繞這個“對齊”機制來工作的。
結構體內存對齊的基本規則
結構體的大小不等于成員變量大小之和,這是因為編譯器會在適當的位置插入填充字節(padding),使得每個成員都滿足自己的對齊要求。
立即學習“C++免費學習筆記(深入)”;
常見的對齊規則包括:
- 每個成員的起始地址是其自身對齊值的整數倍
- 結構體整體的大小是對齊值最大的那個成員的整數倍
- 對齊值通常是類型的大小,比如int是4字節,則默認按4字節對齊;但也可以被修改
舉個例子:
struct Example { char a; // 1字節 int b; // 4字節 short c; // 2字節 };
假設在32位系統下,默認對齊方式為4字節:
- a占1字節,放在0偏移處沒問題;
- b要求從4的倍數開始,所以1~3是填充字節;
- c要求從2的倍數開始,當前偏移是8(剛好符合),占用2字節;
- 整體結構體大小要對齊到最大成員的對齊值(即4),所以最后可能還有2字節填充,總大小為12。
結構體填充是怎么發生的?
填充主要發生在兩個地方:
- 成員之間:前面的成員不能滿足下一個成員的對齊要求時,中間插入填充
- 結構體末尾:整個結構體的大小如果不是最大對齊值的整數倍,就補上填充
填充的目的不是浪費空間,而是為了訪問速度優化。例如,一個int如果被拆成兩次讀取,效率會大打折扣。
另外,結構體順序不同會導致填充也不同。比如把char放最后,結構體大小可能會變小:
struct Example2 { int b; // 4字節 short c; // 2字節 char a; // 1字節 };
此時填充量更少,結構體總大小可能是8而不是12。
所以設計結構體的時候,盡量按照對齊大小從大到小排列成員,可以減少填充,節省內存。
alignas關鍵字的作用與使用場景
C++11引入了alignas關鍵字,用于顯式指定某個變量或結構體的對齊方式。
它可以用來:
- 強制某個變量以更大的對齊方式存儲
- 控制結構體整體的對齊方式
- 配合SIMD指令、內存池等底層操作
語法很簡單:
alignas(16) int x; // x按16字節對齊 struct alignas(16) MyStruct { int a; double b; };
上面的例子中,即使MyStruct本身只需要8字節對齊,但由于用了alignas(16),整個結構體都會按16字節對齊。這對于某些需要嚴格對齊的場合非常有用,比如向量計算、DMA傳輸等。
需要注意的是:
- alignas的參數必須是2的冪次
- 如果多個alignas同時出現,會選擇最大的那個
- 使用不當可能導致內存浪費,但能提升訪問效率
小結一下
內存對齊是C++中不可忽視的一個細節,特別是在做高性能或嵌入式開發時。結構體填充雖然看起來像“浪費”,但它是為了保證訪問效率和平臺兼容性。通過合理安排結構體成員順序、使用alignas關鍵字,可以更好地控制內存布局,避免不必要的空間浪費或性能損失。
基本上就這些。