内核" container_of" - 任何使ISO符合标准的方法?

时间:2012-04-22 16:17:25

标签: c linux-kernel linked-list c99 standards-compliance

在查看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;
}

因此,只有指向list的指针,才能获得指向blabla(并获得“value”)的指针。 对于我的问题,我如何使其尽可能便携(符合C89 / C99的最佳情况?)。由于使用了typeof(),这只是gcc。

这是我到目前为止所得到的:

#define container_of(ptr, type, member) (                  \
                (type *) (char *)(ptr)-offsetof(type,member)\
                )

此代码段是否符合ISO标准(因此应该可以在任何符合标准的编译器上编译)?

3 个答案:

答案 0 :(得分:16)

正如Ouah评论的那样,({ ... })语句表达式是GNU扩展;你将无法使用它。您的核心表达式接近所需,但没有足够的括号:

#define container_of(ptr, type, member) \
                      ((type *) ((char *)(ptr) - offsetof(type, member)))

这看起来很干净。它只分散在两条线上。

答案 1 :(得分:12)

宏的编写方式与对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)))

答案 2 :(得分:3)

带有类型检查的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’

我们得到26的distinct pointer types警告,但不是25.这是我们对指针被误用的诊断。

我首先尝试将类型检查放在逗号运算符的左侧,gcc抱怨它没有效果,这是一个麻烦。但是通过使其成为操作数,我们确保使用它。

ISO {C}没有很好地定义&((type *) 0)->member技巧,但它被广泛用于定义offsetof。如果你的编译器对offsetof使用这个空指针技巧,它几乎肯定会在你自己的宏中表现出来。