内核的“container_of” – 任何使ISO符合标准的方法吗?
在查看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; }
因此,只有指向列表的指针,您才能获得指向blabla(并获得“value”)的指针。 对于我的问题,我如何使其尽可能便携(符合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)))
这看起来很干净。 SO只分布在两条线上。
宏的编写方式是对ptr
进行类型检查。 如果编译器不兼容gcc,则可以使用复合文字而不是语句表达式,并回退到指针的简单检查,而不是使用__typeof__
:
#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 #include 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'
我们得到26的distinct pointer types
警告,但不是25.这是我们对指针被误用的诊断。
我首先尝试将类型检查放在逗号运算符的左侧,gcc抱怨没有效果,这是一个麻烦。 但是通过使其成为操作数,我们确保使用它。
&((type *) 0)->member
技巧没有被ISO C很好地定义,但它被广泛用于定义offsetof
。 如果你的编译器对offsetof
使用这个空指针技巧,它几乎肯定会在你自己的宏中表现出来。