C offsetof宏如何工作?
可能重复:
为什么这个C代码有效?
你如何在struct上使用offsetof()?
我在互联网上读到了这个偏移宏,但它没有解释它的用途。
#define offsetof(a,b) ((int)(&(((a*)(0))->b)))
它想做什么以及使用它有什么好处?
它没有优点,不应该使用,因为它调用未定义的行为(并使用错误的类型 – int
而不是size_t
)。
C标准在stddef.h
定义了一个偏移宏,它实际上适用于需要结构中元素偏移的情况,例如:
#include 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文件时。
R ..在回答问题的第二部分时是正确的:使用现代C编译器时不建议使用此代码。
但要回答问题的第一部分,这实际上是做什么的:
( (int)( // 4. &( ( // 3. (a*)(0) // 1. )->b ) // 2. ) )
从内到外,这是……
- 将值零转换为struct指针类型
a*
- 获取此(非法放置)struct对象的struct字段b
- 获取此
b
字段的地址 - 将地址转换为
int
从概念上讲,这是将一个struct对象放在内存地址为零,然后找出特定字段的地址是什么。 这可以让您找出结构中每个字段的内存偏移量,这样您就可以编写自己的序列化器和反序列化器来将结构转换为字节数组和从字节数组转换结构。
当然,如果您实际取消引用零指针,程序将崩溃,但实际上一切都在编译器中发生,并且在运行时没有实际的零指针被取消引用。
在大多数原始系统中,C运行int
的大小是32位,并且与指针相同,所以这实际上是有效的。
它正在查找struct
的特定成员的字节偏移量。 例如,如果您具有以下结构:
struct MyStruct { double d; int i; void *p; };
然后你有offsetOf(MyStruct, d) == 0
, offsetOf(MyStruct, i) == 8
, offsetOf(MyStruct, p) == 12
(也就是说,名为d
的成员从0开始是0字节结构等)。
它的工作方式是假装你的结构实例存在于地址0( ((a*)(0))
部分),然后它获取目标结构成员的地址并将其转换为整数。 虽然解除引用地址0处的对象通常是一个错误,但是可以使用该地址,因为地址运算符&
和成员解除引用->
相互抵消。
它通常用于通用序列化框架。 如果您有用于在某种线数据(例如文件中的字节或网络中的字节)和内存数据结构之间进行转换的代码,那么创建从成员名到成员偏移的映射通常很方便,这样您就可以序列化或者以通用方式反序列化值。
offsetof宏的实现实际上是无关紧要的。
实际的C标准将其定义为7.17.3:
offsetof(type, member-designator)
它扩展为一个整数常量表达式,其类型为size_t,其值是以字节为单位的偏移量,从结构的开头(由类型指定)到结构成员(由member-designator指定)。 类型和成员指示符应为给定static type t;
。
相信Adam Rosenfield的回答。
R是完全错误的,它有很多用途 – 特别是能够分辨出代码在平台之间是不可移植的。
(好吧,它是C ++,但是我们在静态模板编译时断言中使用它来确保我们的数据结构不会改变平台/版本之间的大小。)