#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})linux
該宏在Linux內核代碼(版本2.6.22)中定義以下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER);
分析:
(TYPE *)0,將 0 強制轉換爲 TYPE 型指針,記 p = (TYPE *)0,p是指向TYPE的指針,它的值是0。那麼 p->MEMBER 就是 MEMBER 這個元素了,而&(p->MEMBER)就是MENBER的地址,而基地址爲0,這樣就巧妙的轉化爲了TYPE中的偏移量。再把結果強制轉 換爲size_t型的就OK了,size_t其實也就是int。
typedef __kernel_size_t size_t;
typedef unsigned int __kernel_size_t;
可見,該宏的做用就是求出MEMBER在TYPE中的偏移量。
數據結構
關於typeof,這是gcc的C語言擴展保留字,用於聲明變量類型.
const typeof( ((type *)0->member ) *__mptr = (ptr);意思是聲明一個與member同一個類型的指針常量 *__mptr,並初始化爲ptr.也就是該數據結構體中通用鏈表成員變量的地址。即member的入口地址
(type *)( (char *)__mptr - offsetof(type,member) );意思是__mptr的地址減去member在該struct中的偏移量獲得的地址, 再轉換成type型指針. 該指針就是member的入口地址了.
在一個數據結構體變量中,通用鏈表結構體成員變量所在的地址(也就是member的入口地址)減去通用鏈表結構體成員變量在該數據結構體變量中的在偏移量,獲得的結果就是該數據結構體變量的入口地址。
spa
通用鏈表的應用實例:指針
#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/list.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Xie"); MODULE_DESCRIPTION("List Module"); MODULE_ALIAS("List module"); struct student { char name[100]; int num; struct list_head list; }; struct student *pstudent; struct student *tmp_student; struct list_head student_list; struct list_head *pos; int mylist_init(void) { int i = 0; INIT_LIST_HEAD(&student_list); pstudent = kmalloc(sizeof(struct student)*5,GFP_KERNEL); memset(pstudent,0,sizeof(struct student)*5); for(i=0;i<5;i++) { sprintf(pstudent[i].name,"Student%d",i+1); pstudent[i].num = i+1; list_add( &(pstudent[i].list), &student_list); } list_for_each(pos,&student_list) { tmp_student = list_entry(pos,struct student,list); printk("<0>student %d name: %s\n",tmp_student->num,tmp_student->name); } return 0; } void mylist_exit(void) { int i ; /* 實驗:將for換成list_for_each來遍歷刪除結點,觀察要發生的現象,並考慮解決辦法 */ for(i=0;i<5;i++) { list_del(&(pstudent[i].list)); } kfree(pstudent); } module_init(mylist_init); module_exit(mylist_exit);