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; } 例二: 它的作用顯而易見,那就是根據一個結構體變數中的一個域成員變數的指標來獲取指向整個結構體變數的指標。比如,有一個結構體變數,其定義如下:    1. struct demo_struct {    2.     type1 member1;    3.     type2 member2;    4.     type3 member3;    5.     type4 member4;    6. };    7.    8. struct demo_struct demo; 同時,在另一個地方,獲得了變數demo中的某一個域成員變數的指標,比如:    1. 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)根據巨集的定義進行展開如下:    1. struct demo_struct *demop = ({                      \    2.     const typeof( ((struct demo_struct *)0)->member3 ) *__mptr = (memp);    \    3.     (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中,其定義如下:    1. 24#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 同樣,我們將上述的offsetof呼叫展開,即為:    1. (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實現了根據一個結構體變數中的一個域成員變數的指標來獲取指向整個結構體變數的指標的功能。