虽然看着Linux内核的实现双向链表圆形名单,我发现下面的宏:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
其工作原理是返回指针安排其成员之一的只给出地址:
struct blabla
{
int value;
struct list_head *list;
}
因此,你可以得到指针布拉布拉(并获得“值”)给出的只有指针列表。 我的问题,我怎么会做出这种尽可能实现可移植性(最好的情况下,符合C89 / C99?)。 由于typeof运算()的使用,这仅仅是GCC。
这是我到目前为止有:
#define container_of(ptr, type, member) ( \
(type *) (char *)(ptr)-offsetof(type,member)\
)
难道这个片段符合ISO标准(因此应该能够在任何符合编译器编译)?
如Ouah评论的,所述({ ... })
语句表达是GNU扩展; 你将无法使用。 你的核心表现是接近真实需要什么,但是没有足够的括号:
#define container_of(ptr, type, member) \
((type *) ((char *)(ptr) - offsetof(type, member)))
这看上去干净给我。 这只是两行流传这么久。
宏写它是perfom一个类型检查的方式ptr
。 它可以使用复合文字,而不是语句表达,并回落到一个简单的检查为指针,而不是使用的__typeof__
如果编译器不GCC-兼容:
#ifdef __GNUC__
#define member_type(type, member) __typeof__ (((type *)0)->member)
#else
#define member_type(type, member) const void
#endif
#define container_of(ptr, type, member) ((type *)( \
(char *)(member_type(type, member) *){ ptr } - offsetof(type, member)))
ISO C90兼容版本的类型检查。 (但是, 需要注意:两种评估ptr
!)
#define container_of(ptr, type, member) \
((type *) ((char *) (ptr) - offsetof(type, member) + \
(&((type *) 0)->member == (ptr)) * 0))
struct container {
int dummy;
int memb;
};
#include <stddef.h>
#include <stdio.h>
int main()
{
struct container c;
int *p = &c.memb;
double *q = (double *) p;
struct container *pc = container_of(p, struct container, memb);
struct container *qc = container_of(q, struct container, memb);
return 0;
}
测试:
$ gcc -Wall containerof.c
containerof.c: In function ‘main’:
containerof.c:20:26: warning: comparison of distinct pointer types lacks a cast
containerof.c:20:21: warning: unused variable ‘qc’
containerof.c:19:21: warning: unused variable ‘pc’
我们得到了distinct pointer types
为26的警告,而不是25。关于被滥用的指针是我们的诊断。
我首先尝试将所述类型检查到逗号操作者的左手侧,GCC抱怨具有没有影响,这是一个麻烦。 但是,使它成为一个操作数,我们确保其使用。
的&((type *) 0)->member
特技不能很好地由ISO C定义的,但它被广泛用于定义offsetof
。 如果你的编译器对这个空指针招offsetof
,它几乎肯定会表现自己在自己的宏。