#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);