# C語言宏函數container_of()怎么使用
## 引言
在Linux內核開發中,`container_of()`宏是一個極其重要且常用的工具。它能夠通過結構體成員的地址反向推導出整個結構體的起始地址,這種技術在鏈表、設備驅動等場景中廣泛應用。本文將深入解析`container_of()`的原理、使用方法以及實際應用案例。
---
## 一、container_of()宏概述
### 1.1 什么是container_of()
`container_of()`是Linux內核中定義的一個宏,其作用是通過結構體中某個成員的地址,計算出該結構體的起始地址。這種技術在內核數據結構(如鏈表、設備驅動模型)中被頻繁使用。
### 1.2 宏定義源碼
```c
// Linux內核中的定義(簡化版)
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
offsetof(type, member)
是一個標準庫宏,用于計算結構體成員相對于結構體起始地址的偏移量。例如:
struct sample {
int a;
char b;
double c;
};
// 假設double在64位系統偏移量為8
size_t offset = offsetof(struct sample, c); // 可能返回8
以container_of(&obj->member, struct obj, member)
為例:
1. 通過typeof
獲取成員的類型并做類型檢查
2. 使用offsetof
計算成員偏移量
3. 將成員指針轉換為char*
(確保字節級計算)
4. 減去偏移量得到結構體起始地址
內核鏈表list_head
的典型用法:
struct task_struct {
int pid;
struct list_head tasks; // 嵌入的鏈表節點
};
// 通過tasks節點獲取task_struct
struct task_struct *task = container_of(ptr, struct task_struct, tasks);
#include <stdio.h>
#include <stddef.h>
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
struct student {
int id;
char name[20];
float score;
};
int main() {
struct student stu = {101, "Alice", 95.5};
float *score_ptr = &stu.score;
// 通過score成員反推student結構體
struct student *s = container_of(score_ptr, struct student, score);
printf("ID: %d, Name: %s\n", s->id, s->name);
return 0;
}
輸出結果:
ID: 101, Name: Alice
typeof
和指針強制轉換確保了類型安全。如果錯誤地傳遞了成員名,編譯時會報錯。
原始實現依賴GCC擴展語法({...})
,在其他編譯器中可能需要改寫:
// 替代方案
#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
container_of()
在運行時只有簡單的指針運算,沒有性能開銷。
對于多層嵌套的結構體,可以鏈式調用:
struct A {
struct B b;
};
struct B {
int val;
};
struct B *b_ptr = &obj.a.b;
struct A *a_ptr = container_of(b_ptr, struct A, b);
#define get_container(ptr, member) \
container_of(ptr, typeof(*ptr), member)
C++中可以通過:
reinterpret_cast<T*>(reinterpret_cast<char*>(ptr) - offsetof(T, member))
實現類似功能,但缺乏編譯時類型檢查。
這種技術實現了類似”子類通過基類指針向上轉型”的效果。
在kernel/sched/core.c
中,通過task_struct
的se
成員獲取任務指針:
struct task_struct *p = container_of(se, struct task_struct, se);
struct device
嵌入到具體設備結構中時,常用此宏反向引用。
container_of()
宏展示了C語言指針操作的強大能力,其核心思想可以歸納為:
1. 通過成員地址減去偏移量得到基地址
2. 依賴編譯器的類型檢查保證安全
3. 在資源受限環境下實現高效的對象定位
掌握這個宏對于理解Linux內核和開發高性能C程序至關重要。
#ifndef container_of
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
注意:實際內核代碼還包含額外的編譯器屬性檢查,本文為簡化說明進行了適當裁剪。 “`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。