我继承了很多代码,我对这种扩展感到磕磕绊绊。我理解它的作用,我不明白它是如何做到的。
#define OFFSETOF(structure, item) ((u16)&(((structure*)0)->item))
更具体地说,除了这部分((structure*)0)
之外,我得到了大部分内容。我不知道零正在做什么或者它是什么。
如果你需要知道,'结构'只是typedef struct{}
,而项目是
struct{
u32 my_example_item;
}
再次,零点让我感到困惑。
答案 0 :(得分:2)
structure
是一种类型(structure*)
是一个指针(structure*)0
是structure*
->item
获取指向item
成员
(((structure*)0)->item)
假装在内存中位置0有一个structure
,并引用它的项目。&(((structure*)0)->item)
获取该项的地址,该地址将等于对象中item
的偏移量。此地址的类型为u32*
(因为item
是u32
)((u16)&(((structure*)0)->item))
将该地址转换为u16
,因此它是适当的偏移类型。答案 1 :(得分:1)
这类似于C标准头offsetof()
中定义的<stddef.h>
宏的常见实现。代码实际上具有未定义的行为,但它可能适用于大多数C实现。
它从类型item
的开头产生structure
的偏移量(以字节为单位)。 structure
应该是一个结构类型(它可以是一个联合,但是OFFSETOF
总是会产生0),而item
是该结构(或联合)类型的成员。
(structure*)0
是指向structure
的类型的空指针。
((structure*)0)->item
是指空指针指向的地址中不存在的item
对象的structure
成员。是的,这很奇怪。 如果将空指针表示为内存地址0,并且item
的偏移量为12字节,则((structure*)0)->item
引用的对象位于内存地址这对当前系统的内存模型做了很多假设。这些假设通常是有效的;如果他们不这样做,这件事可能会在你脸上爆炸。
&(((structure*)0)->item)
是地址12处神话物体的地址。
最后,((u16)&(((structure*)0)->item))
是显式转换为structure*
的内存地址12(类型u16
)。据推测,u16
是无符号的16位整数类型(如果是这样,使用标准uint16_t
会更有意义。)
因此,给定结构类型structure
和偏移量为12的成员item
,此宏将扩展为产生值12的表达式。
请记住,地址(指针值)不是数字;他们在概念上是截然不同的概念。此宏仅适用于使用特定内存模型的系统,该模型不是由C标准指定的。
我不确定为什么结果属于u16
类型。标准offsetof
宏会生成size_t
类型的值。
对于使用不同内存模型的系统(例如,如果空指针表示为all-bits-one或0xdeadbeef
),则此OFFSETOF
宏将不起作用,并且标准{ {1}}宏必须适合在特定系统上工作。这部分原因offsetof
位于标准库中,因此可以以适用于每个系统的方式实现。