首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Linux內核container_of宏深度解析:GNU C語句表達式防止副作用的設計智慧

     編輯:程式設計綜合問答

宏定義

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

為什麼不用簡化版?

#define container_of_simple(ptr, type, member) \
    (type *)( (char *)ptr - offsetof(type,member) )

看起來功能相同,但**忽略了副作用風險**。

GNU C 語句表達式

`({ ... })` 是 GCC 擴展,允許在表達式位置寫入**復合語句**(含變量聲明、循環、控制流),整個塊的值為最後一條語句的值。

int x = ({ int a = 3; int b = 5; a + b; }); // x = 8

container_of 逐行解析

const typeof( ((type *)0)->member ) *__mptr = (ptr);
// 1. ((type *)0)->member : 以 0 地址為基址,訪問 member 偏移,類型為 member 的類型
// 2. typeof(...) : 獲取 member 的類型(如 int、struct ListHead* 等)
// 3. const ... *__mptr = (ptr) : 定義一個指向 member 類型的常量指針,賦值為 ptr
//    → 關鍵:__mptr 是**臨時變量**,只求值一次,避免 ptr 為有副作用的表達式(如 func()、i++)被多次求值

(type *)( (char *)__mptr - offsetof(type,member) );
// 4. 將 __mptr 轉為 char*,減去 member 在 type 中的偏移量,得到 type 結構體起始地址
// 5. 強轉為 type*,作為整個語句表達式的值返回

副作用防護演示

// 危險場景:ptr 為函數調用
struct Node *p = container_of(get_node(), struct Node, list);
// 若用簡化版:
// (type *)((char *)get_node() - offsetof(...))
// get_node() 會被調用**兩次**!導致邏輯錯誤、性能損耗、甚至崩潰

// container_of 保證 get_node() 只調用一次,結果賦給 __mptr 復用

類似模式:內核 min/max 宏

#define min(x, y) ({ \
    typeof(x) _x = (x); \
    typeof(y) _y = (y); \
    (void)(&_x == &_y); /* 類型檢查 */ \
    _x < _y ? _x : _y; })

// 防止 min(a++, b++) 導致 a/b 增加兩次

總結

  • `container_of` 利用 **GNU C 語句表達式** + **臨時變量** 實現**單次求值**、**類型安全**、**無副作用**
  • 這是內核級代碼的**工程智慧**:在宏展開階段解決 C 語言缺乏函數參數求值策略控制的缺陷
  • 用戶態代碼若不涉及副作用表達式,簡化版亦可;但養成使用 `typeof` + 臨時變量的習慣更安全
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved