C offsetof宏如何工作?

时间:2011-10-26 01:47:39

标签: c macros offset

  

可能重复:
  Why does this C code work?
  How do you use offsetof() on a struct?

我在互联网上读到了这个偏移宏,但它没有解释它的用途。

#define offsetof(a,b) ((int)(&(((a*)(0))->b)))

尝试做什么以及使用它有什么好处?

4 个答案:

答案 0 :(得分:29)

R ..在回答问题的第二部分时是正确的:使用现代C编译器时不建议使用此代码。

但是要回答你问题的第一部分,这实际上是做什么的:

(
  (int)(         // 4.
    &( (         // 3.
      (a*)(0)    // 1.
     )->b )      // 2.
  )
)

从内到外工作,这是......

  1. 将值零转换为结构指针类型a*
  2. 获取此(非法放置)结构对象的结构字段b
  3. 获取此b字段的地址
  4. 将地址转换为int
  5. 从概念上讲,这是将一个struct对象放在内存地址为零,然后找出特定字段的地址是什么。这可以让您找出结构中每个字段的内存偏移量,这样您就可以编写自己的序列化器和反序列化器来将结构转换为字节数组。

    当然,如果您实际取消引用零指针,程序将崩溃,但实际上一切都在编译器中发生,并且在运行时没有实际的零指针解除引用。

    在大多数原始系统中,C以int的大小运行是32位并且与指针相同,所以这实际上有效。

答案 1 :(得分:12)

它没有优点,不应该使用,因为它调用未定义的行为(并使用错误的类型 - int而不是size_t)。

C标准在offsetof中定义stddef.h宏,它实际上适用于需要结构中元素偏移的情况,例如:

#include <stddef.h>

struct foo {
    int a;
    int b;
    char *c;
};

struct struct_desc {
    const char *name;
    int type;
    size_t off;
};

static const struct struct_desc foo_desc[] = {
    { "a", INT, offsetof(struct foo, a) },
    { "b", INT, offsetof(struct foo, b) },
    { "c", CHARPTR, offsetof(struct foo, c) },
};

可让您以编程方式按名称填写struct foo字段,例如在阅读JSON文件时。

答案 2 :(得分:4)

它找到struct的特定成员的字节偏移量。例如,如果您具有以下结构:

struct MyStruct
{
    double d;
    int i;
    void *p;
};

然后你有offsetOf(MyStruct, d) == 0offsetOf(MyStruct, i) == 8offsetOf(MyStruct, p) == 12(也就是说,名为d的成员是从结构开始的0字节,等等。 )。

它的工作方式是假装你的结构实例存在于地址0(((a*)(0))部分),然后它获取目标结构成员的地址并将其转换为整数。虽然取消引用地址0处的对象通常会出错,但可以使用该地址,因为地址操作符&和成员解除引用->相互抵消。

它通常用于通用序列化框架。如果您有用于在某种有线数据(例如文件中的字节或网络中的字节)和内存数据结构之间进行转换的代码,那么创建从成员名到成员偏移的映射通常很方便,这样您就可以序列化或者以通用方式反序列化值。

答案 3 :(得分:-2)

offsetof宏的实现确实无关紧要。

实际C标准将其定义为7.17.3:

offsetof(type, member-designator)

扩展为一个整数常量表达式,其类型为size_t,其值是以字节为单位的偏移量,从结构的开头(由类型指定)到结构成员(由mem​​ber-designator指定)。类型和成员指示符应为static type t;

相信Adam Rosenfield的回答。

R是完全错误的,它有很多用途 - 特别是能够分辨出代码在平台之间何时不可移植。

(好吧,它是C ++,但是我们在静态模板编译时断言中使用它来确保我们的数据结构不会改变平台/版本之间的大小。)