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

C語言 sizeof(struct) 深度解密:內存對齊、#pragma pack 與跨平台移植指南

     編輯:關於C

為什麼 sizeof(struct) ≠ 成員大小之和?

面試經典題:struct { char a; short b; char c; int d; },新手答 8,實際輸出 12/16。核心原因:**內存對齊**。

內存對齊的根本動因

  1. 硬件訪問效率:CPU 從內存讀取數據以「字」為單位(32 位系統 4 字節,64 位 8 字節)。未對齊訪問需兩次總線周期 + 拼接,嚴重拖慢性能。
  2. 原子操作需求:某些架構要求原子讀寫的地址必須對齊。
  3. 總線协议:總線地址線設計決定了對齊邊界。

對齊規則正式化

  1. 成員對齊:第 i 個成員偏移量為 min(sizeof(成員類型), 指定對齊數) 的整數倍。
  2. 結構體整體對齊:結構體總大小為 所有成員對齊參數的最大值 的整數倍(不足補齊)。
  3. 嵌套結構體:外層結構體對齊參考內層結構體的 最大成員對齊值(而非內層總大小)。

經典演示:地址偏移驗證


struct Test {
    char a;      // offset 0, size 1
    short b;     // offset 2 (2字節對齊), size 2
    char c;      // offset 4, size 1
    int d;       // offset 8 (4字節對齊), size 4
}; // 總大小 12 (最大對齊 4 的倍數)

特殊情況:char 類型「隨和」


struct {
    char a, b, c; // 連續 0,1,2
    short d;      // offset 4 (不跨越 4 字節邊界)
    int e;        // offset 8
}; // 總大小 12

char 佔 1 字節,放哪裡都單次訪問,故編譯器傾向緊湊排列。

#pragma pack(n) 精確控制對齊

編譯器指令,改變默認對齊參數(通常為 4/8)。

核心規則:最小原則

成員有效對齊 = min(成員自身大小, pack(n) 指定值)

嵌套結構體實戰


#pragma pack(8)

struct Inner {
    char a;   // 對齊 1, offset 0
    long b;   // 對齊 min(4,8)=4, offset 4
}; // sizeof=8 (最大對齊 4 的倍數)

struct Outer {
    char c;           // 對齊 1, offset 0
    struct Inner in;  // 對齊 min(內層最大成員對齊=4, 8)=4, offset 4
    long long d;      // 對齊 min(8,8)=8, offset 16 (12→16 對齊8)
}; // sizeof=24

#pragma pack() // 恢復默認

驗證程序


#include 

#pragma pack(8)
struct Inner { char a; long b; };
struct Outer { char c; struct Inner in; long long d; };
#pragma pack()

int main() {
    struct Outer o;
    printf("c:%p in.a:%p in.b:%p d:%p\n",
           (void*)&o.c - (void*)&o,
           (void*)&o.in.a - (void*)&o,
           (void*)&o.in.b - (void*)&o,
           (void*)&o.d - (void*)&o);
    printf("sizeof(Outer)=%zu\n", sizeof(struct Outer));
    return 0;
}
// 輸出: c:0 in.a:4 in.b:8 d:16 sizeof=24

跨平台移植陷阱與對策

陷阱現象對策
默認對齊不同32位 4字節,64位 8字節網絡/文件協議显式 #pragma pack(1) 或手動序列化
長整型大小不同Windows 64位 long=4,Linux 64位 long=8使用 stdint.h 固定寬度類型 (int32_t, int64_t)
指針大小不同32位 4字節,64位 8字節結構體中避免裸指針,用句柄/索引
位域佈局未定義高低位順序、跨邊界行為位域僅用於本地硬件映射,禁用於跨平台數據交換

最佳實踐清單

  1. 成員排序:按類型大小遞減排列(大→小),自然減少填充。如 double, int, short, char。
  2. 固定寬度類型:#include ,用 uint32_t 替代 unsigned int。
  3. 協議結構體:#pragma pack(1) + 手動添加填充字段保證對齊,或使用序列化庫。
  4. 靜態斷言驗證:_Static_assert(sizeof(Struct) == EXPECTED, "layout changed"); (C11)。
  5. 文檔化佈局:關鍵結構體添加偏移量註釋,便於維護與調試。

總結

sizeof(struct) 不是簡單加法,而是**硬件架構、編譯器實現、語言標準**三方博弈的結果。理解對齊本質,善用 #pragma pack、stdint.h、成員排序、靜態斷言,才能寫出高性能、可移植、無驚喜的 C 代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved