C offsetof 宏如何工作?

发布于 2024-12-11 22:57:02 字数 452 浏览 0 评论 0原文

可能的重复:
为什么这段 C 代码可以工作?
如何在结构上使用 offsetof() ?< /p>

我在互联网上阅读有关此offsetof宏的信息,但它没有解释它的用途。

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

它想要做什么以及使用它的优势是什么?

Possible Duplicate:
Why does this C code work?
How do you use offsetof() on a struct?

I read about this offsetof macro on the Internet, but it doesn't explain what it is used for.

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

What is it trying to do and what is the advantage of using it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

凝望流年 2024-12-18 22:57:03

它没有任何优点,也不应该使用,因为它会调用未定义的行为(并使用错误的类型 - int 而不是 size_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) },
};

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

It has no advantages and should not be used, since it invokes undefined behavior (and uses the wrong type - int instead of size_t).

The C standard defines an offsetof macro in stddef.h which actually works, for cases where you need the offset of an element in a structure, such as:

#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) },
};

which would let you programmatically fill the fields of a struct foo by name, e.g. when reading a JSON file.

情独悲 2024-12-18 22:57:03

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

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

那么您将具有 offsetOf(MyStruct, d) == 0offsetOf(MyStruct, i) == 8,并且offsetOf(MyStruct, p) == 12 (即,名为 d 的成员距结构体开头 0 个字节,等等)。

它的工作方式是假装结构的实例存在于地址 0(((a*)(0)) 部分),然后获取预期结构成员的地址并将其转换为整数。虽然取消引用地址 0 处的对象通常会出现错误,但获取该地址是可以的,因为取址运算符 & 和成员取消引用 -> 会取消每个对象的引用其他出。

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

It's finding the byte offset of a particular member of a struct. For example, if you had the following structure:

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

Then you'd have offsetOf(MyStruct, d) == 0, offsetOf(MyStruct, i) == 8, and offsetOf(MyStruct, p) == 12 (that is, the member named d is 0 bytes from the start of the structure, etc.).

The way that it works is it pretends that an instance of your structure exists at address 0 (the ((a*)(0)) part), and then it takes the address of the intended structure member and casts it to an integer. Although dereferencing an object at address 0 would ordinarily be an error, it's ok to take the address because the address-of operator & and the member dereference -> cancel each other out.

It's typically used for generalized serialization frameworks. If you have code for converting between some kind of wire data (e.g. bytes in a file or from the network) and in-memory data structures, it's often convenient to create a mapping from member name to member offset, so that you can serialize or deserialize values in a generic manner.

神经暖 2024-12-18 22:57:03

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

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

offsetof(type, member-designator)

它扩展为类型为 size_t 的整型常量表达式,其值是以字节为单位的偏移量,到结构体成员(由 member-designator 指定),从其结构(按类型指定)。类型和成员指示符应为给定的static type t;

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

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

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

The implementation of the offsetof macro is really irrelevant.

The actual C standard defines it as in 7.17.3:

offsetof(type, member-designator)

which expands to an integer constant expression that has type size_t, the value of which is the offset in bytes, to the structure member (designated by member-designator), from the beginning of its structure (designated by type). The type and member designator shall be such that given static type t;.

Trust Adam Rosenfield's answer.

R is completely wrong, and it has many uses - especially being able to tell when code is non-portable among platforms.

(OK, it's C++, but we use it in static template compile time assertions to make sure our data structures do not change size between platforms/versions.)

雅心素梦 2024-12-18 22:57:02

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

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

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

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

  1. 将值零转换为结构指针类型 a*
  2. 获取结构字段这个(非法放置的)struct 对象的 b
  3. 获取这个 b 字段的地址
  4. 将地址转换为 int

从概念上讲,这是将一个 struct 对象放置在内存地址 0 处,然后查找特定字段的地址是什么。这可以让您计算出结构中每个字段在内存中的偏移量,以便您可以编写自己的序列化器和反序列化器来将结构与字节数组相互转换。

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

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

R.. is correct in his answer to the second part of your question: this code is not advised when using a modern C compiler.

But to answer the first part of your question, what this is actually doing is:

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

Working from the inside out, this is ...

  1. Casting the value zero to the struct pointer type a*
  2. Getting the struct field b of this (illegally placed) struct object
  3. Getting the address of this b field
  4. Casting the address to an int

Conceptually this is placing a struct object at memory address zero and then finding out at what the address of a particular field is. This could allow you to figure out the offsets in memory of each field in a struct so you could write your own serializers and deserializers to convert structs to and from byte arrays.

Of course if you would actually dereference a zero pointer your program would crash, but actually everything happens in the compiler and no actual zero pointer is dereferenced at runtime.

In most of the original systems that C ran on the size of an int was 32 bits and was the same as a pointer, so this actually worked.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文