首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

C 語言記憶體對齊原理深度解析:結構體填充、#pragma pack 與可變參數宏

     編輯:VC 教學

一、什麼是位元組對齊?為何需要?

現代 CPU 從記憶體讀取資料時,若資料位址符合其類型大小的倍數(如 int 位址為 4 的倍數),可單一總線週期完成;否則需兩次讀取並拼湊,嚴重影響效能。某些架構(如 SPARC、ARM 舊版)甚至直接觸發硬體例外。編譯器因此自動在結構體成員間插入填充位元組,確保每個成員位址滿足對齊需求。

二、預設對齊規則 (VC6.0 / 32-bit x86)

類型大小對齊要求 (位址模值)
char11
short22
int / long / float44
double88
指標44

結構體整體大小 必須為「最大成員對齊值」的倍數,尾部亦會填充。

三、實例演示

案例 A:double, char, int

struct A { double d; char c; int i; };
// 佈局:
// offset 0-7  : d (8 bytes, aligned 8)
// offset 8    : c (1 byte)
// offset 9-11 : padding (3 bytes) -> align int to 4
// offset 12-15: i (4 bytes)
// 總大小 = 16 (8 的倍數)

案例 B:char, double, int (順序調換)

struct B { char c; double d; int i; };
// offset 0    : c (1)
// offset 1-7  : padding (7) -> align double to 8
// offset 8-15 : d (8)
// offset 16-19: i (4)
// 尾部 padding 4 bytes -> 總大小 24 (8 的倍數)
// 填充總計 11 bytes!

結論:成員排序影響記憶體佔用,建議按大小遞減排列。

四、#pragma pack(n) 自訂對齊

規則

  • n >= sizeof(member) → 依預設對齊。
  • n < sizeof(member) → 以 n 為對齊單位。
  • 結構體總大小:若 n 大於所有成員 → 最大成員對齊值倍數;否則為 n 的倍數。

範例:#pragma pack(4)

#pragma pack(push)
#pragma pack(4)
struct Test { char m1; double m4; int m3; };
#pragma pack(pop)
// 佈局 (4-byte alignment):
// m1: offset 0 (1 byte)
// padding 3 -> offset 4
// m4: offset 4 (8 bytes)  // 4 < 8, 但 pack(4) 允許 4-byte align for double? 
// 實測 VC: double 仍要求 8-byte align? 
// 修正:VC 中 #pragma pack(n) 對基本類型仍受限於其自然對齊,除非 n >= 自然對齊。
// 實際 VC6 行為:pack(4) 時 double 仍靠 8,故大小 24。
// pack(1) 時才會強制緊湊。

更正:MSVC 中 #pragma pack(n) 實際行為為 取 min(n, 自然對齊)。若 n=4,double 自然對齊 8,取 min(4,8)=4 → 允許 4-byte 對齊。但實測 VC6.0 pack(4) 下 double 仍置於 offset 8(8-byte align),故建議以 pack(1) 強制緊湊,或依賴自然對齊。

五、巢狀結構體對齊

#pragma pack(8)
struct S1 { char a; long b; }; // a:1, pad3, b:4 -> size 8 (max align 4, but pack 8 -> total multiple of 8)
struct S2 { char c; S1 d; long long e; };
// c: offset 0 (1)
// pad 3 -> offset 4
// d: offset 4 (size 8, align 4) -> occupies 4-11
// e: offset 12 -> needs 8 align -> pad 4 -> offset 16 (8 bytes)
// total 24 (multiple of 8)
#pragma pack()

六、Minix stdarg.h 可變參數對齊宏

#define __va_rounded_size(TYPE) \
  (((sizeof(TYPE) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
// 等價位運算寫法:
#define __va_rounded_size(TYPE) \
  ((sizeof(TYPE) + sizeof(int) - 1) & ~(sizeof(int) - 1))

原理:sizeof(int)=4。將任意型別大小向上取整至 4 的倍數。對應 x86 呼叫慣例中參數在堆疊上以 4-byte 對齊推入。

  • sizeof(TYPE) >= 4 → (size+3)/4*4 向上取整。
  • sizeof(TYPE) < 4 → (1+3)/4*4 = 4,char/short 亦佔 4 bytes。

七、實務建議

  1. 結構體成員按大小遞減排序,減少填充。
  2. 網路封包、檔案格式、共享記憶體 必須使用 #pragma pack(1) 並手動處理對齊,確保跨平台一致。
  3. 使用 offsetof、static_assert (C11) 驗證佈局。
  4. 64 位元環境指標 8 bytes,long 可能 8 bytes (LP64),重新檢視對齊假設。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved