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

C/C++ 計算結構體成員偏移量:offsetof 宏原理與實戰應用

     編輯:關於C++

為何需要偏移量?

  • 插件/模組架構:核心定義介面結構,插件自定義結構佈局,核心透過偏移量存取插件資料,實現二進制相容。
  • 序列化/反序列化:不依賴成員名稱,直接按位元組偏移讀寫。
  • 底層驅動/硬體暫存器映射:將結構體映射到固定記憶體位址。

標準宏 offsetof (C++11 <cstddef>)

#include <cstddef>
struct Foo { char c; int i; double d; };
size_t off_i = offsetof(Foo, i); // 標準、可攜、編譯期常數

宏實現原理 (編譯器內建或手工模擬)

// 經典實現 (GCC/MSVC 內建通常更安全)
#define offsetof(type, member) ((size_t)&(((type*)0)->member))

// MSVC 64 位元加強版 (處理虛繼承等複雜情況)
#ifdef _WIN64
#define offsetof(s,m) (size_t)( (ptrdiff_t)&reinterpret_cast((((s*)0)->m)) )
#else
#define offsetof(s,m) (size_t)&(((s*)0)->m)
#endif

核心技巧:將 0 轉為 type*,存取 member,取其位址 → 即為相對於結構體起始的位元組偏移。未定義行為警告:解參考空指標屬 UB,但編譯器對此模式有特殊處理,實際不產生指令。

記憶體對齊實戰演示

#include <iostream>
#include <cstddef>
using namespace std;

struct AlignDemo {
    char c;     // offset 0, size 1
    char b;     // offset 1, size 1
    // padding 2 bytes (對齊到 4)
    int e;      // offset 4, size 4
    int e1;     // offset 8, size 4
    // padding 4 bytes (對齊到 8)
    double d;   // offset 16, size 8
}; // sizeof = 24

int main() {
    cout << "offsetof(c) = " << offsetof(AlignDemo, c) << '\n';  // 0
    cout << "offsetof(b) = " << offsetof(AlignDemo, b) << '\n';  // 1
    cout << "offsetof(e) = " << offsetof(AlignDemo, e) << '\n';  // 4
    cout << "offsetof(e1) = " << offsetof(AlignDemo, e1) << '\n'; // 8
    cout << "offsetof(d) = " << offsetof(AlignDemo, d) << '\n';  // 16
    cout << "sizeof = " << sizeof(AlignDemo) << '\n';            // 24
    return 0;
}

實戰案例:FFmpeg AVOption 機制

FFmpeg 選項系統使用偏移量將通用選項表映射到各編解碼器私有結構:

#define OFFSET(x) offsetof(AVCodecContext, x)

static const AVOption options[] = {
    {"b", "bitrate", OFFSET(bit_rate), AV_OPT_TYPE_INT64, ...},
    {"flags", "flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, ...},
    // ...
};

解析器通用邏輯:char* ptr = (char*)ctx + opt->offset; *(int64_t*)ptr = value;,無需知曉 AVCodecContext 完整定義。

進階:編譯期偏移量與靜態斷言

static_assert(offsetof(Foo, i) == 4, "Layout changed!");
static_assert(sizeof(Foo) == 24, "Padding changed!");

確保跨平台、跨編譯器版本的二進制介面穩定 (ABI)。

總結

  • 優先使用標準 offsetof (C++11 <cstddef>),可攜且安全。
  • 理解對齊規則:成員偏移 = 前一成員結束位置向上取整到該類型對齊數倍數。
  • 偏移量是「結構體版反射」的基石,廣泛用於序列化、插件系統、驅動開發。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved