list_entry详解

list_entry宏分析

该宏为list.h中比较难懂的其内核源码为

#define list_entry(ptr, type, member) \

((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

 

我们从一个例子看这个宏把,以下是我写的一个代码:

#include <stdio.h>

#include <stdlib.h>

struct foobar

{


unsigned int foo;

long bar;

long boo;

};

int main()

{

struct foobar tmp;

printf("address is tmp is = %p\n" ,&tmp);

printf("address is tep->foo = %p\t offset of tmp->foo= %lu\n",&tmp.foo,(unsigned long)&((struct foobar *)0)->foo);

printf("address is tep->foo = %p\t offset of tmp->foo= %lu\n",&tmp.foo,(unsigned long)&((struct foobar *)1)->foo);

printf("address is tep->bar = %p\t offset of tmp->bar= %lu\n", &tmp.bar,(unsigned long)&((struct foobar *)0)->bar);

printf("address is tep->boo = %p\t offset of tmp->boo= %lu\n", &tmp.boo,(unsigned long)&((struct foobar *)0)->boo);

printf("addr1= %p\t addr2= %p\n",(char *)&tmp.bar, &((struct foobar *)0)->bar );

printf("struct start is %p\n", (struct foobar *) (((char *)&tmp.bar)-((unsigned long) &((struct foobar *)0)->bar)));

printf("struct start is %p\n", (struct foobar *) (((unsigned long *)&tmp.bar)-((unsigned long) &((struct foobar *)0)->bar)));

return 0;

}

其运行结果为:

第2行和第3行的代码运行结果可以看出使用0转化为结构体指针和使用1转化的结果;

使用0地址转化为结构体指针,该结构体得到的首地址为0 ,取出该结构体成员的某一成员,等价于取出了该成员变量的相对于结构体首地址的偏移量

最后两行的结果是为了证明转换为char指针进行相减可以得到正却结果,原因是char指针地址每次减1其地址减1,但使用无符号整型每次地址会减2,原因是指针减1,则相应地址减去指针类型所占空间,所以结果是错误的

以上的宏也是通过同样的原理实现的,其中ptr当前的遍历到list节点,type包含list_head为结构体名称,number为 type结构体中list_head字段的名称,通过绝对地址((type *)((char *)(ptr)  =》当前ptr指针的地址,即当前遍历到的list_head指针地址;相对地址(unsigned long)(&((type *)0)->member)))=》该type类型中相对于list_head成员的偏移量;绝对地址减去相对地址实现的