list_entry() 源代码解析
2012-04-12 21:44
267 查看
本文出自 “little白”
博客:/article/7358639.html 和http://www.flatws.cn/article/program/linux/2011-05-23/27127.html 并在此基础上进行解释。
以下是原文:
list_entry这样定义:
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
解释:list_entry()
是一个宏命令,只要找到member的地址,名称,以及结构体的类型,该函数就能找到该结构体的首地址。
先看&((type
*)0)->member:
把“0”强制转化为指针类型,则该指针一定指向“0”(数据段基址)。因为指针是“type *”型的,所以可取到以“0”为基地址的一个type型变量member域的地址。那么这个地址也就等于member域到结构体基地址的偏移字节数。
再来看 ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))):
(char *)(ptr)使得指针的加减操作步长为一字节,(unsigned long)(&((type *)0)->member)等于ptr指向的member到该member所在结构体基地址的偏移字节数。二者一减便得出该结构体的地址。转换为 (type *)型的指针,大功告成。
下面举个例子说明下:
typedef struct
{
int i;
int j
} exp;
这个exp结构体占用8个字节.
假设声明一个变量。
exp e1;
那么假如已知 e1.j
的地址,想知道 e1 的地址该如何办呢?只要知道 j 在 e1 中的偏移,然后把 j 的地址减去这个偏移就是 e1 的地址了。在这里, i 占据的前4个字节,所以 j 占据了5-8的字节。
现在我们用 list_entry
来解释一下。
int *p = &e1.j;
假设 e1 的地址是
0x100,那么 p 就是 0x104。可是如何才能比较方便的知道 p 减去 4 就是 e1 的地址呢?尤其是我们可能有时候不知道 exp 这个结构体里面具体什么样子的。使用 list_entry(p,
exp, j),则变成:
(exp *)((char *)p-(unsigned long)(&((exp *)0)->j))
(exp *)0
在 0 地址上面建立 8 个字节的 exp 结构体, ->j 取出这个0地址上exp结构体里的j成员, &((exp *)0)->j) 把这个成员地址取出来,由于 j 在这个结构体里是在5-8字节,所以从0地址数5个字节就是 j 所在的位置。这种方法省去了我们需要预 先知道结构体具体什么样子,结构体里成员的位置怎么安排的。p 的地址再减去我们刚算出来j所在的位置,就得到 e1 的地址。
欢迎批评指正!
博客:/article/7358639.html 和http://www.flatws.cn/article/program/linux/2011-05-23/27127.html 并在此基础上进行解释。
以下是原文:
list_entry这样定义:
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
解释:list_entry()
是一个宏命令,只要找到member的地址,名称,以及结构体的类型,该函数就能找到该结构体的首地址。
先看&((type
*)0)->member:
把“0”强制转化为指针类型,则该指针一定指向“0”(数据段基址)。因为指针是“type *”型的,所以可取到以“0”为基地址的一个type型变量member域的地址。那么这个地址也就等于member域到结构体基地址的偏移字节数。
再来看 ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))):
(char *)(ptr)使得指针的加减操作步长为一字节,(unsigned long)(&((type *)0)->member)等于ptr指向的member到该member所在结构体基地址的偏移字节数。二者一减便得出该结构体的地址。转换为 (type *)型的指针,大功告成。
下面举个例子说明下:
typedef struct
{
int i;
int j
} exp;
这个exp结构体占用8个字节.
假设声明一个变量。
exp e1;
那么假如已知 e1.j
的地址,想知道 e1 的地址该如何办呢?只要知道 j 在 e1 中的偏移,然后把 j 的地址减去这个偏移就是 e1 的地址了。在这里, i 占据的前4个字节,所以 j 占据了5-8的字节。
现在我们用 list_entry
来解释一下。
int *p = &e1.j;
假设 e1 的地址是
0x100,那么 p 就是 0x104。可是如何才能比较方便的知道 p 减去 4 就是 e1 的地址呢?尤其是我们可能有时候不知道 exp 这个结构体里面具体什么样子的。使用 list_entry(p,
exp, j),则变成:
(exp *)((char *)p-(unsigned long)(&((exp *)0)->j))
(exp *)0
在 0 地址上面建立 8 个字节的 exp 结构体, ->j 取出这个0地址上exp结构体里的j成员, &((exp *)0)->j) 把这个成员地址取出来,由于 j 在这个结构体里是在5-8字节,所以从0地址数5个字节就是 j 所在的位置。这种方法省去了我们需要预 先知道结构体具体什么样子,结构体里成员的位置怎么安排的。p 的地址再减去我们刚算出来j所在的位置,就得到 e1 的地址。
欢迎批评指正!
相关文章推荐
- 详解Linux内核之双向循环链表(2) list_for_each/list_entry/list_for_each_entry
- List for each entry
- ConcurrentHashMap Segment&HashEntryLinkedList
- list_entry的宏定义
- linux核list_for_each_entry(pos, head, member)分析
- list_entry()
- linux内核学习笔记之——list_for_each_entry
- list_entry()宏
- list_entry()等价于container_of()
- list_for_each_entry
- 内核双链表 list_entry实现
- 内核链表中list_entry()函数
- containof和list_entry的实现方式
- list_entry()详解
- list_entry和list_for_each分析
- list_entry 解析
- list_entry()详解
- list_entry 解析
- list_entry()宏
- EFI LIST_ENTRY