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

发布于 2024-11-23 15:07:05 字数 995 浏览 5 评论 0原文

可能的重复:
来自的 'offsetof' 宏是否存在;调用未定义的行为?
取消引用空指针

    #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附近的地址空间。我说过:获取成员的地址不会访问、触摸或读取该成员的值。根据标准,它是完全安全的。

    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 附近的内存?

我知道有一种安全的方法来计算标准提供的偏移量,但我很好奇你对此有何看法。所有人都赞成我的解释:对这个问题投赞成票:-)

Possible Duplicates:
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);

I had a little chat/conversation with some fellow users, and one of them said that this is dereferencing and accessing the address space near address NULL. I said: taking an address of a member will not access, touch, or read the value of that member. According to standard it is completely safe.

    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!

Is this always a safe way to calculate offset, or are compilers free to dereference and mess up the memory near address NULL according to standards?

I know there is a safe way to calculate offsets provided by the standard, but I am curious what you have to say about this. All in favor of my explenation: up-vote this question :-)

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

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

发布评论

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

评论(5

恏ㄋ傷疤忘ㄋ疼 2024-11-30 15:07:05

您不会在这里取消引用任何无效的内容。该宏所做的只是告诉编译器,内存中地址 NULL 处存在 p_type 类型的结构。然后它获取p_member的地址,它是这个虚构结构的成员。因此,在任何地方都不会取消引用。

事实上,这正是 offsetof 宏的内容,在 stddef.h 中定义。

编辑:
正如一些评论所说,这可能不适用于 C++ 和继承,我只在 C 中将 offsetof 与 POD 结构一起使用。

You're not dereferencing anything invalid here. All that macro does is tell the compiler that a structure of type p_type exists in memory at the address NULL. It then takes the address of p_member, which is a member of this fictitious structure. So, no dereferencing anywhere.

In fact, this is exactly what the offsetof macro, defined in stddef.h does.

EDIT:
As some of the comments say, this may not work well with C++ and inheritance, I've only used offsetof with POD structures in C.

紫罗兰の梦幻 2024-11-30 15:07:05

绝对不是。甚至通过向 NULL 添加偏移量来创建指针也会调用未定义行为。更有动力的人可以从规范中挖掘章节和诗句。

顺便说一句,无论您想要计算这些偏移量的原因是什么,它都可能是一个糟糕的原因。

Absolutely not. To even create a pointer by adding an offset to NULL is to invoke Undefined Behavior. Someone more motivated can dig up chapter and verse from the spec.

By the way, whatever your reason is for wanting to compute these offsets, it is a probably a bad one.

っ左 2024-11-30 15:07:05

这是无效的 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/ 如果一元 & 的操作数 [运算符] 是一元 * 的结果
运算符,既不是该运算符,也不是 &运算符被评估并且
结果就像两者都被省略了,除了对
运算符仍然适用,并且结果不是左值。

It is invalid C++.

From ISO/IEC 14882:2003, 5.2.5:

3/ If E1 has the type “pointer to class X,” then the expression E1->E2
is converted to the equivalent form (*(E1)).E2 (...)

However, there has been a defect report about this, and it is valid C99 (and probably valid C++0x too):

From ISO/IEC 9899:1999, 6.5.3:

2/ If the operand [of the unary & operator] is the result of a unary *
operator, neither that operator nor the & operator is evaluated and
the result is as if both were omitted, except that the constraints on
the operators still apply and the result is not an lvalue.

望喜 2024-11-30 15:07:05

所以 &p->b&(p->b) 是(根据定义)&((*p).b),这似乎确实涉及在获取成员之前取消对 p 的引用。即使它违反了标准,它也可以在大多数编译器上运行。正如评论中所指出的,这项工作可能在涉及多重继承的情况下工作正常。

您想通过偏移量来解决什么问题?您可以使用引用、指针或成员指针来代替吗?

So &p->b is &(p->b) is (by definition) &((*p).b), which does seem to involve a dereference of p before getting the member. It may work on most compilers though even if it violates the standard. As noted in a comment this work probably work right in cases involving multiple inheritance.

What problem are you trying to solve by getting this offset? Could you use references, pointers, or pointers-to-member instead?

述情 2024-11-30 15:07:05
#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 的起始地址,因此这将为您提供偏移量。这实际上是一个非常巧妙的方法。

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

Is the same as (after preprocessing)

struct a { int a, b; };
size_t l = (size_t)&(((struct a *)NULL)->b);

I see that you are casting NULL to a pointer-to-struct a, and then getting the address of its member b.

As far as I know, since you are getting the address of b, and not actually accessing or modifying (dereferencing) the value of b, the compiler will not complain, and you will not get a runtime error. Since NULL (or 0) is the starting address of a, this will give you the offset. This is actually a pretty nifty way to do that.

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