【问题标题】:C: What does this macro mean?C:这个宏是什么意思?
【发布时间】:2011-11-21 04:30:09
【问题描述】:

你怎么看这个宏的第二行? (type *)0 在这种情况下是什么意思?

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

【问题讨论】:

  • 请注意,这使用了两个 gcc 扩展:({ }) 用于评估在表达式中具有局部变量的块,typeof 用于声明成员类型的变量。跨度>

标签: c gcc macros


【解决方案1】:

您正在查找((type *)0)->member类型。它实际上并没有取消引用指针(我告诉你,那太疯狂了。疯狂!

这是 C 的一个奇怪之处。如果他们写 typeof(type.member) 可能会更有意义,但遗憾的是这是不允许的。

【讨论】:

  • 这不是“C 的怪异”,而是 GCC 的怪异。 typeof 是非标准扩展(例如 GCC)。
  • 这很有趣,我从来不知道。虽然,我想 gcc 是罪魁祸首我并不感到震惊。
  • 我知道 0/NULL 在这里有意义,但为什么不是 typeof( ((type *)123)->member )? 0 是否和任何一样好,或者可能还有更多内容,例如类型转换,但 0 未定义?我知道offsetof 是必须的,但它与typeof 的用法不同。
【解决方案2】:

一个更易读的版本:

#define container_of(ptr, type, member) (                  \
   {                                                       \
      const typeof( ((type *)0)->member ) *__mptr = (ptr); \
      (type *)( (char *)__mptr - offsetof(type,member) );  \
   }                                                       \
)

简而言之,宏定义了一个可用的“测试”,可以插入到 if 和其他语句中。 ((type *) 0) 将 NULL 引用转换为相关类型,然后获取该类型的“成员”子组件。 typeof() 宏将返回与成员子“对象”关联的类型。所以它创建了一个与 type.member 子组件相同类型的常量 __mptr 变量。例如,如果我们有:

typedef struct foo_s {
   int bogus;
   int bar;
} foo;

如果这样调用:

foo blah;  /* and initialize it of course */
int *myptr = &foo.bar;

foo *result = container_of(myptr, foo, bar);

那么宏的第一行会变成下面这样:

const int *__mptr = (myptr);

宏的第二行然后计算原始结构的内存位置并返回结构的内存指针并将其正确地转换为该结构,展开后将如下所示:

(foo *)( (char *)__mptr - offsetof(foo, bar));

结果是这样的:

foo *result = container_of(myptr, foo, bar);

允许您获取结构中的 myptr 元素并从中提取指向原始容器的指针。

现在,这在上面的示例中没有用,因为您已经可以访问包含结构。但假装你没有,因为你所在的 API 没有通过它。这个宏是获取父容器的一种棘手方法,而您通常不会使用它。

当然,更好的做法是在不需要这种 hack 的情况下构建更好的 API。但是如果你c它会很有用

【讨论】:

  • 韦斯,这个解释真的帮助我理解了发生了什么。谢谢!!
【解决方案3】:

(type *)0 本质上是NULL。 第二行取你给它的值的地址,并减去它在结构中的位置。这给了你结构的地址

【讨论】:

    【解决方案4】:

    这就像 offsetof(type, member) 是如何定义的。

    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

    (TYPE *)0 将 0 强制转换为指向 TYPE 类型 0 的指针,((TYPE *)0)->member 指向 TYPE 的成员。

    【讨论】:

    • 编译器实现可以像这样定义offsetof,但通常不会使用其他一些内部技巧。
    【解决方案5】:

    其实第二行是多余的,只是用来做类型检查的。

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

    将具有相同的语义,除了类型检查功能。

    看:Rationale behind the container_of macro in linux/list.h

    一般解释另见:http://www.kroah.com/log/linux/container_of.html

    【讨论】:

      猜你喜欢
      • 2013-05-16
      • 2015-01-09
      • 2015-11-03
      • 2014-04-25
      • 2011-02-09
      • 1970-01-01
      • 2012-01-05
      • 2011-11-13
      • 1970-01-01
      相关资源
      最近更新 更多