如何理解“((size_t)&((TYPE *)0) - > MEMBER)”?

时间:2013-09-01 02:45:24

标签: c kernel

linux-2.6.16 / include / linux / stddef.h中的代码是:

 #undef offsetof
 #ifdef __compiler_offsetof
 #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
 #else
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 #endif

#define DNAME_INLINE_LEN (sizeof(struct dentry)-offsetof(struct dentry,d_iname))

如何理解“((size_t)&((TYPE *)0) - > MEMBER)”?
谢谢

3 个答案:

答案 0 :(得分:11)

操作顺序可以通过以下方式说明:

    1.                         0
    2.                ((TYPE *)0)
    3.              ( ((TYPE *)0)->MEMBER )
    4.             &( ((TYPE *)0)->MEMBER )
    5.  ( (size_t) &( ((TYPE *)0)->MEMBER )
  1. 一切都以数字0开头。
  2. 0被转换为指向struct TYPE的指针。 IOW,我们让编译器相信程序数据段的开头有这样一个struct(这通常是危险的,但我稍后会提到为什么不在这种特殊情况下)。
  3. 然后,引用此MEMBER中的成员struct
  4. 其内容未使用,但其地址(使用&),
  5. 此地址已转换回数字类型size_t
  6. 由于结构的“开始”的地址被指定为0MEMBER的地址(转换为数字时)是结构中的偏移量。

    这个特定代码无害的原因是没有写入任何内存位置,甚至没有访问过。一切都只涉及指向这些位置(但不包括其内容)和数字的指针。所有这些都保存在机器寄存器或通常的本地堆栈中。

    以下表达式:

          ( (size_t) &( ((TYPE *)3264)->MEMBER ) - 3264 )
    

    也会奏效。添加了括号集以使其更清晰。

答案 1 :(得分:3)

这是一个hack,它允许您在没有编译器支持的情况下模拟offsetof的功能:它利用MEMBER的地址等于其基址的基数的事实。设为零。

考虑这个例子:

struct Test {
    char text[32];
    int count;
}

如果struct Test分配在0xC000地址,则text的地址为0xC000count的地址为{{} 1}}。但是,如果基址为零(标准不允许),则0xC020的地址将为零,并且计数的地址将为text。将这些地址转换为0x20会为您提供相应成员的偏移量。

答案 2 :(得分:0)

它返回指定结构成员的偏移量,使其偏离地址0.首先,它将地址0强制转换为正确的类型(即(TYPE*)强制转换),然后添加偏移量指定的成员到该基地址。由于基址为0,因此您可以有效地获得在宏中指定的成员的偏移量。