可能重复:
Does the 'offsetof' macro from <stddef.h> invoke undefined behaviour?
dereferencing the null pointer
#define _OFFS_OF_MEMBER(p_type, p_member) (size_t)&(((p_type *)NULL)->p_member)
struct a
{
int a, b;
};
size_t l = _OFFS_OF_MEMBER(struct a, b);
我和其他一些用户进行了一些聊天/对话,其中一个人说这是取消引用并访问地址空间附近的地址空。我说:取一个成员的地址不会访问,触摸或读取该成员的价值。根据标准,它是完全安全的。
struct a* p = NULL;
size_t offset = &p->b; // this may NOT touch b, it is not dereferencing
// p->b = 0; // now, we are dereferincing: acccess violation time!
这是否总是一种安全的计算偏移量的方法,或者编译器是否可以根据标准自由地取消引用并将内存附近的内存弄糊涂?
我知道有一种安全的方法来计算标准提供的偏移量,但我很好奇你对此有何看法。所有人都赞成我的言论:向上投票这个问题: - )
答案 0 :(得分:2)
绝对不是。要通过向NULL添加偏移量来创建指针,就可以调用未定义的行为。更有动力的人可以从规范中挖掘章节和经文。
顺便说一下,无论你想要计算这些偏移的原因是什么,它都可能是一个糟糕的。
答案 1 :(得分:2)
你在这里没有取消引用任何无效的东西。宏所做的就是告诉编译器,地址p_type
的内存中存在NULL
类型的结构。然后它采用p_member
的地址,这是这个虚构结构的成员。所以,不要在任何地方取消引用。
实际上,这正是stddef.h
中定义的offsetof
宏所做的。
修改强>
正如一些评论所说,这可能不适用于C ++和继承,我只在C中使用了offsetof
和POD结构。
答案 2 :(得分:1)
它是无效的C ++。
来自ISO / IEC 14882:2003,5.2.5:
3 /如果E1具有“指向类X的指针”类型,则表达式E1-> E2 转换为等效形式(*(E1))。E2(...)
然而,there has been a defect report about this,它是有效的C99(也可能是有效的C ++ 0x):
来自ISO / IEC 9899:1999,6.5.3:
2 /如果[一元&amp;的操作数] operator]是一元*的结果 操作员,既不是操作员也不是&amp;操作员被评估和 结果就好像两个都被省略了,除了约束 运算符仍然适用,结果不是左值。
答案 3 :(得分:0)
所以&p->b
&(p->b)
是&((*p).b)
(根据定义)p
,在获得会员之前似乎涉及{{1}}的解除引用。它可能适用于大多数编译器,即使它违反了标准。正如评论中所指出的,这项工作可能适用于涉及多重继承的案例。
你想通过获得这个偏移来解决什么问题?您可以使用引用,指针或指向成员的指针吗?
答案 4 :(得分:0)
#define _OFFS_OF_MEMBER(p_type, p_member) (size_t)&(((p_type *)NULL)->p_member)
struct a
{
int a, b;
};
size_t l = _OFFS_OF_MEMBER(struct a, b);
与(预处理后)
相同struct a { int a, b; };
size_t l = (size_t)&(((struct a *)NULL)->b);
我看到你将NULL转换为指向结构的指针a,然后获取其成员b的地址。
据我所知,既然你得到了b的地址,而没有实际访问或修改(解除引用)b的值,编译器就不会抱怨,也不会出现运行时错误。由于NULL(或0)是a的起始地址,因此它将为您提供偏移量。这实际上是一种非常好的方式。