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

Linux 內核 container_of 宏深度解析:從成員指針獲取結構體指針

     編輯:關於C語言

問題背景

C 語言無「類」概念,但內核大量使用「嵌入式結構體」模擬繼承:

struct file {
    struct list_head f_list;  // 嵌入鏈表節點
    // ... 其他成員
};

給定 struct list_head *ptr,如何反推包含它的 struct file *?

container_of 宏定義

#define container_of(ptr, type, member) ({                      \
    const typeof(((type *)0)->member) *__mptr = (ptr);          \
    (type *)((char *)__mptr - offsetof(type, member)); })

三大核心組件逐層拆解

1. offsetof 宏:計算成員偏移量

#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
  • (TYPE *)0:將地址 0 強轉為類型指針,構造一個「虛擬對象」位於地址 0。
  • &((TYPE *)0)->MEMBER:取該虛擬對象中 MEMBER 的地址,即成員相對結構體首地址的字節偏移。
  • 結果為 size_t,編譯期常量,無運行開銷。

2. typeof 擴展:獲取表達式類型

  • GCC 擴展,typeof(expr) 返回 expr 的類型。
  • typeof(((type *)0)->member) 獲得 member 的聲明類型(如 struct list_head)。

3. 完整推導過程

// 假設調用:container_of(ptr, struct file, f_list)
const typeof(((struct file *)0)->f_list) *__mptr = ptr;
// 等價於:struct list_head *__mptr = ptr;  // 類型安全賦值

// 計算容器地址:
(char *)__mptr - offsetof(struct file, f_list)
// 成員地址 - 成員偏移 = 結構體首地址
// 強轉回目標類型指針
(struct file *)((char *)__mptr - offsetof(struct file, f_list))

圖解內存佈局

struct file 實例地址: 0xA000
+------------------+ 0xA000
| 其他成員...      |
+------------------+ 0xA010  ← f_list 偏移 0x10
| struct list_head |  ← ptr 指向這裡 (0xA010)
|   next, prev     |
+------------------+ 0xA018

offsetof = 0x10,ptr = 0xA010,0xA010 - 0x10 = 0xA000 即容器地址。

實戰測試代碼

#include 
#include   // offsetof

#define container_of(ptr, type, member) ({                      \
    const typeof(((type *)0)->member) *__mptr = (ptr);          \
    (type *)((char *)__mptr - offsetof(type, member)); })

struct student {
    char name[20];
    char sex;
    int age;
} stu = {"ZhangSan", 'M', 20};

int main() {
    // 通過 &stu.sex 反推 stu 地址
    struct student *p = container_of(&stu.sex, struct student, sex);
    printf("name: %s, sex: %c, age: %d\n", p->name, p->sex, p->age);

    // 驗證 offsetof
    printf("offsetof(sex) = %zu\n", offsetof(struct student, sex));  // 輸出 20
    return 0;
}

應用場景與注意事項

  • 內核鏈表遍歷:list_for_each(pos, head) { my_struct *p = container_of(pos, my_struct, list); ... }
  • 多態模擬:基類指針轉換為具體子類指針。
  • 類型安全:typeof 確保 ptr 與 member 類型匹配,編譯期報錯。
  • 非標準 C:依賴 GCC 擴展(typeof、語句表達式 ({...})),移植需適配(如 MSVC 用 CONTAINING_RECORD)。
  • 成員必須非位域:位域無法取地址,offsetof 無效。

MSVC 等效宏

#define CONTAINING_RECORD(address, type, field) \
    ((type *)((char *)(address) - offsetof(type, field)))

無 typeof 檢查,使用者需自行保證類型正確。

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