宏的C偏移量是如何工作的?

宏的C偏移量是如何工作的?,c,macros,offset,C,Macros,Offset,可能重复: 我在网上读到了这个宏的offsetof,但它没有解释它的用途 #define offsetof(a,b) ((int)(&(((a*)(0))->b))) 它试图做什么?使用它有什么好处?它没有好处,也不应该使用,因为它调用了未定义的行为(并且使用了错误的类型-int,而不是size\u t) C标准在stddef.h中定义了一个offsetof宏,该宏在需要结构中某个元素的偏移量的情况下实际起作用,例如: #include <stddef.h> s

可能重复:

我在网上读到了这个宏的offsetof,但它没有解释它的用途

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

它试图做什么?使用它有什么好处?

它没有好处,也不应该使用,因为它调用了未定义的行为(并且使用了错误的类型-
int
,而不是
size\u t

C标准在
stddef.h
中定义了一个
offsetof
宏,该宏在需要结构中某个元素的偏移量的情况下实际起作用,例如:

#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) },
};
#包括
结构foo{
INTA;
int b;
char*c;
};
结构描述{
常量字符*名称;
int型;
尺寸过大;
};
静态const struct struct_desc foo_desc[]={
{“a”,INT,offsetof(struct foo,a)},
{“b”,INT,offsetof(struct foo,b)},
{“c”,CHARPTR,offsetof(struct foo,c)},
};

这将允许您通过名称以编程方式填充
结构foo
的字段,例如在读取JSON文件时。

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

struct MyStruct
{
    double d;
    int i;
    void *p;
};
然后你会有
offsetOf(MyStruct,d)==0
offsetOf(MyStruct,i)==8
,和
offsetOf(MyStruct,p)==12
(也就是说,名为
d
的成员距离结构的开头是0字节,以此类推)

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


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

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

(
  (int)(         // 4.
    &( (         // 3.
      (a*)(0)    // 1.
     )->b )      // 2.
  )
)
从里到外工作,这是

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

    当然,如果你真的去引用一个零指针,你的程序会崩溃,但实际上所有的事情都发生在编译器中,并且在运行时没有去引用任何实际的零指针


    在大多数原始系统中,C以int的大小运行,其大小为32位,与指针相同,因此这实际上是可行的。

    宏offsetof的实现实际上是不相关的

    实际C标准在7.17.3中对其进行了定义:

    offsetof(type, member-designator)
    
    它扩展为一个整型常量表达式,该表达式具有类型size_t,其值是结构成员(由成员标识符指定)从其结构(由类型指定)的开头到结构成员(由类型指定)的偏移量(以字节为单位)。类型和构件代号应为给定的<规范>静态类型t

    相信亚当·罗森菲尔德的答案

    R是完全错误的,它有很多用途——特别是能够判断代码何时在平台之间不可移植


    (OK,它是C++,但是我们在静态模板编译时断言中使用它来确保我们的数据结构不会改变平台/版本之间的大小)。< /P> < <代码> >宏>从代码> STDDEF.H./C> >不调用UB。定义自己的hack以这种方式计算偏移量会调用UB。@阿德里安:他没有说,“定义自己的宏版本会导致未定义的行为。”他特别说,“定义自己的hack以这种方式计算偏移量会调用UB。”在代码中,此时:

    ((a*)(0))->
    您通过取消引用null调用了未定义的行为。@AdrianCornish:只要实现了正确的行为,就允许实现定义
    偏移量。您的应用程序没有此权限,因为它无法定义任何行为;它只能使用已定义的语言构造。这就是C的工作原理。6.5.2.3没有使用“取消引用”一词,而是将其指定为“第一个表达式指向的对象的命名成员”。由于
    (a*)(0)
    未指向
    a
    类型的对象,因此行为未定义(由于未定义)。您引用的文本与此无关。在伪宏中,没有指向“不完整”或“对象类型”的指针转换为指向“无效”的指针。该
    offsetof
    宏不正确。他们应该强制转换为
    size\u t
    ,而不是
    int
    ,并且他们可能应该在强制转换之前从结果中减去
    (char*)0
    ,即使它是一个空指针常量。问题是C,我们仍然必须使用
    struct MyStruct
    )杰出的非常感谢。对我来说,关键是
    将一个struct对象放在内存地址0处,然后找出特定字段的地址是什么