list_entry: 原来C 程序可以这样写

简介: list_entry: 原来C 程序可以这样写 最近还是在看代码,越看越觉蹊跷和有意思。 遇到一些list相关的问题,本来以为也就是双向循环链表的基本操作呢,结果。。。 list_entry这样定义: #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) 解释:找到成员member所在容器的地址。

list_entry: 原来C 程序可以这样写

最近还是在看代码,越看越觉蹊跷和有意思。
遇到一些list相关的问题,本来以为也就是双向循环链表的基本操作呢,结果。。。
list_entry这样定义:
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
解释:找到成员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 的地址。也就是:
&e1 == list_entry(p, exp, j)
开眼界了! 原来程序可以这样写! 这段时间的体会是,写那些库函数的才是牛人!
延伸阅读:
深入分析 Linux 内核链表.  http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/

相关文章
|
7月前
|
存储 安全 Java
java集合框架及其特点(List、Set、Queue、Map)
java集合框架及其特点(List、Set、Queue、Map)
使用JavaStream将List转为Map
使用JavaStream将List转为Map
|
6月前
|
存储 Java API
Map.entry详解
Map.entry详解
|
6月前
|
程序员
Map.Entry 类使用简介
Map.Entry 类使用简介
51 1
|
6月前
|
Java
Map.entry方法总结
Map.entry方法总结
|
6月前
|
Java API
Map.entry用法详解
Map.entry用法详解
|
6月前
遍历Map的四种方法之map.entry详解
遍历Map的四种方法之map.entry详解
|
7月前
|
存储 安全 Java
Java集合详解(List、Map、Set)
Java集合详解(List、Map、Set)
75 4
|
7月前
|
Java API
List转Map(id为key,list为value)
List转Map(id为key,list为value)
276 0
|
7月前
|
Java
java8 foreach用法list转map、map转list
java8 foreach用法list转map、map转list