一、什麼是字節對齊
編譯器為了讓 CPU 訪問更快,會把變量放在“合適的地址”上,而不是緊挨着放。
為什麼要對齊?
- CPU 訪問 4 字節對齊的 int 比非對齊快
- 某些平台(如 ARM)不對齊直接崩
- 所以編譯器會自動插空字節(padding)
二、對齊的核心規則
規則 1:成員對齊規則
每個成員的起始地址,必須是它“對齊值”的整數倍
規則 2:結構體整體對齊
結構體總大小,必須是“最大成員對齊值”的整數倍
規則 3:不夠就補 padding
內存佈局只和“類型 + 順序 + 對齊規則”有關,和變量裏存的“具體值”無關
鐵律
結構體大小 ≠ 成員大小簡單相加
1️⃣ 每個成員的起始地址必須是它“對齊值”的整數倍
2️⃣ 整個結構體的大小必須是“最大對齊值”的整數倍
三、計算結構體大小
示例1:
#include <stdio.h>
#include <string.h>
#include <cstdlib>
// 計算結構體大小,內存對齊,按照最大成員對齊
struct Worker { // 定義一個結構體,相當於java的class
char name[10]; // 10
int age; // 4
double salary; // 8
};
int main() {
int size = sizeof(struct Worker);
printf("Size of Worker struct: %d bytes\n", size); // Size of Worker struct: 24 bytes
return 0;
}
排內存:
name: 10字節
age: 按上面説的 規則1(每個成員的起始地址,必須是它“對齊值”的整數倍),10 不是 age 4字節的整數倍,所以要 + 2,此時到 age 時字節總數為 :10 + 2 + 4 = 16 字節
double: 前面 16 字節滿足double8 字節整數倍,所以此時字節數為:16 + 8 = 24 字節所以結構體 Worker 所佔用的內存就是 24 字節
示例2:
#include <stdio.h>
#include <string.h>
#include <cstdlib>
struct date {
int year;
int month;
int day;
};
struct Worker { // 定義一個結構體,相當於java的class
int number; // 4 字節
char name[10]; // 10
int age; // 4
char sex; // 1 字節
struct date hireDate; // 12 字節
};
int main() {
int size = sizeof(struct Worker);
printf("Size of Worker struct: %d bytes\n", size); // Size of Worker struct: 36 bytes
return 0;
}
排內存:
4 + 10 + 2(padding) + 4 + 1 + 3(padding) + 12 = 36 字節
number: 4 name[10] : 4 + 10 = 14 age: 14 不滿足 age 四字節倍數要求,需要補齊後繼續排,14 + 2 + 4 = 20 sex: 20 + 1 = 21 , char最小,其實放哪都可以,一般最小對齊的要放後面,節省內存 date: struct date 的對齊值是 4 , 21 + 3 + 12 = 36 36 滿足最大對齊 4 字節倍數要求,所以 Worker 所佔內存就是 36
調整屬性順序
將對齊要求大的放前面,小的放後面
struct Worker {
int number; // 4 對齊
int age; // 4 對齊
struct date hireDate; // 4 對齊
char name[10]; // 1 對齊
char sex; // 1 對齊
};
number: 4
age: 4 + 4 = 8
hireDate: 4 + 4 + 12 = 20
name: 4 + 4 + 12 + 10 = 30
sex: 4 + 4 + 12 + 10 + 1 = 31最後:因為整個結構體的大小必須是“最大對齊值”的整數倍
所以 31 不是 最大對齊值 4 字節的整數倍,對齊 1 個字節 變成 32
此時結構體 Worker 的大小就是 32 字節!
同樣的屬性,比上面那個排列方式 節省 4 字節內存!
四、最後
對齊要求大的放前面
先把“大型傢俱(高對齊)”擺好
對齊要求小的放後面
最後用“小雜物(char)”填縫