内核的“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使用这个空指针技巧,它几乎肯定会在你自己的宏中表现出来。