这是有效的ANSI C++代码吗?正在尝试在编译时生成结构成员的偏移量[duplicate]

6g8kf2rb  于 2023-03-20  发布在  其他
关注(0)|答案(5)|浏览(357)

此问题在此处已有答案

11年前关闭了。

可能重复:

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);

我和一些同事聊天/交谈过,其中一个说这是解引用和访问地址NULL附近的地址空间。获取成员的地址将不会访问、接触或读取该成员的值。2根据标准,这是完全安全的。

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!

这总是一种安全的计算偏移量的方法吗?还是编译器可以根据标准自由地解引用和弄乱内存近地址NULL?
我知道有一种安全的方法来计算标准提供的补偿,但我很好奇你对此有什么要说的。投票赞成这个问题:-)

tnkciper

tnkciper1#

它是无效的C++。
摘自ISO/IEC 14882:2003第5.2.5节:
3/如果E1的类型为“指向类X的指针”,则表达式E1-〉E2转换为等价形式(*(E1)). E2(...)
然而,有一个关于这个的缺陷报告,它是有效的C99(可能也是有效的C++0x):
根据ISO/IEC 9899:1999第6.5.3节:
2/如果[一元&运算符的]操作数是一元 * 运算符的结果,则该运算符和&运算符都不求值,结果就像两者都被省略了一样,只是运算符的约束仍然适用,并且结果不是左值。

yftpprvb

yftpprvb2#

所以&p->b is &(p->b) is(根据定义)&((*p).b),这看起来确实涉及到在获取成员之前对p的解引用。尽管它违反了标准,但它可能适用于大多数编译器。正如注解中所指出的,这项工作可能适用于涉及多重继承的情况。
你想通过获取这个偏移量来解决什么问题?你能用引用、指针或者指向成员的指针来代替吗?

ct3nt3jp

ct3nt3jp3#

#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的起始地址,这会给予你偏移量。这实际上是一个很好的方法。

11dmarpk

11dmarpk4#

这里你没有解引用任何无效的东西,这个宏所做的只是告诉编译器p_type类型的结构存在于NULL地址,然后它取p_member的地址,这是这个虚构结构的一个成员,所以没有解引用。
事实上,这正是stddef.h中定义的offsetof宏所做的。

编辑:

正如一些评论所说,这可能不适用于C++和继承,我只在C中使用了带有POD结构的offsetof

axr492tv

axr492tv5#

当然不是。甚至通过向NULL添加偏移量来 * 创建 * 指针都是调用Undefined Behavior。更有动力的人可以从规范中挖掘章节和诗句。
顺便说一句,不管你想要计算这些偏移量的原因是什么,这可能是一个糟糕的原因。

相关问题