1.問題:如何經過結構中的某個變量獲取結構自己的指針???
關於container_of見kernel.h中:
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ / const typeof( ((type *)0)->member ) *__mptr = (ptr); / (type *)( (char *)__mptr - offsetof(type,member) );})
container_of在Linux Kernel中的應用很是普遍,它用於得到某結構中某成員的入口地址.
關於offsetof見stddef.h中:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
TYPE是某struct的類型 0是一個假想TYPE類型struct,MEMBER是該struct中的一個成員. 因爲該struct的基地址爲0, MEMBER的地址就是該成員相對與struct頭地址的偏移量.
關於typeof,這是gcc的C語言擴展保留字,用於聲明變量類型.
const typeof( ((type *)0->member ) *__mptr = (ptr);意思是聲明一個與member同一個類型的指針常量 *__mptr,並初始化爲ptr.
(type *)( (char *)__mptr - offsetof(type,member) );意思是__mptr的地址減去member在該struct中的偏移量獲得的地址, 再轉換成type型指針. 該指針就是member的入口地址了.
例一;
container_of宏定義在[include/linux/kernel.h]中:
#define container_of(ptr, type, member) / const typeof( ((type *)0)->member ) *__mptr = (ptr); / (type *)( (char *)__mptr - offsetof(type,member) );
offsetof宏定義在[include/linux/stddef.h]中:
#define offsetof(type, member) ((size_t) &((type *)0)->member)
下面用一個測試程序test.c來講明
#include struct student{ char name[20]; char sex; }stu={"zhangsan",'m'}; main() { struct student *stu_ptr; //存儲container_of宏的返回值 int offset; //存儲offsetof宏的返回值 //下面三行代碼等同於 container_of(&stu.sex,struct student, sex )參數帶入的情形 const typeof(((struct student*)0)->sex) *_mptr = &stu.sex; //首先定義一個 _mptr指針, 類型爲struct student結構體中sex成員的類型 //typeof 爲獲取(((struct student*)0)->sex)的類型,此處此類型爲char //((struct student*)0)在offsetof處講解 offset = (int)(&((struct student *)0)->sex); /*((struct student*)0)爲 把 0地址 強制轉化爲指向student結構體類型的指針 該指針從地址 0 開始的 21個字節用來存放name 與 sex(char name〔20〕與 char sex共21字節) sex存放在第20個字節出(從0字節開始) &((struct student *)0)->sex 取出sex地址(此處即爲20) 並強制轉化爲整形 因此offset爲20,後面的printf結果將證實這一點*/ stu_ptr = (struct student *)((char*)_mptr - offset); /*((char*)_mptr - offset)此處先把_mptr指針轉化爲字符形指針 ( 爲何這麼作呢? 若是_mptr爲整形指針 _mptr - offset 至關於減去 sizeof(int)*offset個字節) 減去 offset值 至關於 獲得_mptr所在結構體的首地址(即stu的地址) 而後咱們把 該地址 強制轉化爲 struct student類型便可正常使用了*/ printf("offsetof stu.sex = %d/n",offset); printf("stu_ptr->name:%s/tstu_ptr->sex:%c/n", stu_ptr->name, stu_ptr->sex); return 0; }
例二:
它的做用顯而易見,那就是根據一個結構體變量中的一個域成員變量的指針來獲取指向整個結構體變量的指針。好比,有一個結構體變量,其定義以下:
linux
struct demo_struct { type1 member1; type2 member2; type3 member3; type4 member4; }; struct demo_struct demo;
同時,在另外一個地方,得到了變量demo中的某一個域成員變量的指針,好比:
type3 *memp = get_member_pointer_from_somewhere();
此時,若是須要獲取指向整個結構體變量的指針,而不單單只是其某一個域成員變量的指針,咱們就能夠這麼作:
1. struct demo_struct *demop = container_of(memp, struct demo_struct, member3);
首先,咱們將container_of(memp, struct demo_struct, type3)根據宏的定義進行展開以下:
struct demo_struct *demop = ({ /
const typeof( ((struct demo_struct *)0)->member3 ) *__mptr = (memp); / (struct demo_struct *)( (char *)__mptr - offsetof(struct demo_struct, member3) );})
其中,typeof是GNU C對標準C的擴展,它的做用是根據變量獲取變量的類型。所以,上述代碼中的第2行的做用是首先使用typeof獲取結構體域變量member3的類型爲 type3,而後定義了一個type3指針類型的臨時變量__mptr,並將實際結構體變量中的域變量的指針memp的值賦給臨時變量__mptr。
假設結構體變量demo在實際內存中的位置以下圖所示:
demo
+-------------+ 0xA000
| member1 |
+-------------+ 0xA004
| member2 |
| |
+-------------+ 0xA010
| member3 |
| |
+-------------+ 0xA018
| member4 |
+-------------+
則,在執行了上述代碼的第2行以後__mptr的值即爲 0xA010。
再看上述代碼的第3行,其中須要說明的是offsetof,它定義在include/linux/stddef.h中,其定義以下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
一樣,咱們將上述的offsetof調用展開,即爲:
(struct demo_struct *)( (char *)__mptr - ((size_t) &((struct demo_struct *)0)->member3) );
可見,offsetof的實現原理就是取結構體中的域成員相對於地址0的偏移地址,也就是域成員變量相對於結構體變量首地址的偏移。
所以,offsetof(struct demo_struct, member3)調用返回的值就是member3相對於demo變量的偏移。結合上述給出的變量地址分佈圖可知,offsetof(struct demo_struct, member3) 將返回0x10。
因而,由上述分析可知,此時,__mptr==0xA010,offsetof(struct demo_struct, member3)==0x10。
所以, (char *)__mptr - ((size_t) &((struct demo_struct *)0)->member3) == 0xA010 - 0x10 == 0xA000,也就是結構體變量demo的首地址(如上圖所示)。
由此,container_of實現了根據一個結構體變量中的一個域成員變量的指針來獲取指向整個結構體變量的指針的功能。