1

在 C 中,结构的第一个元素与结构本身具有相同的地址。如果第一个元素是 POD,C++ 中的非 POD 结构是否也是如此?

例如,给定以下代码:

struct bar
{
    struct bar *p1, *p2;
    unsigned char h;
}

struct foo
{
    struct bar node;
    int a;

    private:
        int x;
};

int main(void)
{
struct foo A;
struct bar *ptr;

ptr = &A.node;

struct foo *n = ((struct foo*)((char*)(ptr)-(unsigned long)(&((struct foo*)0)->node)));

return 0;
}

我收到“对 NULL 对象的非静态数据成员 'foo::node' 的无效访问......也许 offsetof 宏使用不正确”警告。

我的问题是 - 我可以假设“节点”在 foo 的开头(相同的地址),即使这是一个非 POD 结构?

如果是,我可以使用重新解释演员表,我不会收到任何警告:

struct foo *o = reinterpret_cast<struct foo*>(ptr);

那么,如果 C++ 结构以公共 POD 数据开头,那么第一个元素是否会根据标准共享对象的地址?

谢谢

--edit-- 在没有虚拟方法或超类的类中,假设(第一个成员变量的地址)== this 是否安全?被指出是一个可能的答案。它提到“在没有中间访问说明符的情况下声明的(非联合)类的非静态数据成员被分配,以便后面的成员在类对象中具有更高的地址。”这并没有真正解决是否第一个元素在我的情况下,与对象本身的地址相同。

我认为由于标准没有说明任何内容,我不能假设是这样。我可能需要将结构末尾的对象更改为指针,并根据需要处理分配它们。

4

2 回答 2

1

标准在 [class.mem]/19 中说

如果标准布局类对象有任何非静态数据成员,则其地址与其第一个非静态数据成员的地址相同。否则,其地址与其第一个基类子对象(如果有)的地址相同。[注意:因此,标准布局结构对象中可能存在未命名的填充,但不是在其开头,这是实现适当对齐所必需的。——尾注]

因此,如果该类是标准布局类,而您的标准不是标准,则保证第一个成员的地址是该类的地址。在您的情况下,它声明它是它的第一个基类的地址。由于您没有基础对象,这意味着第一个成员共享对象的地址,因为在对象的开头永远不会有填充。这意味着 afoo的地址是它的bar成员的地址,因为它bar是一个标准布局类,它也将是p1.

请注意,在第一个成员之后尝试获取任何成员是未定义的行为。类类型(包括struct)允许在类的任何成员之间进行填充以用于对齐目的。这意味着您永远不知道其他成员相对于第一个成员的确切位置。

于 2017-03-17T14:53:19.670 回答
-4

根据 Slava,因为虚拟表可以是 struct 的一部分,所以简短的回答是否定的。现在 vtables 不是标准的一部分。它们只是实现虚拟事物从而实现多态性的一种机制。出于所有实际原因,答案仍然是否定的。

请看以下代码:

#include <iostream>

using namespace std;

struct bar
{
    struct bar *p1, *p2;
    unsigned char h;
};

struct foo
{
    struct bar node;
    int a;

    private:
        int x;
};

int main(void)
{
    struct foo A;
    struct bar *ptr;

    A.node.p1 = new bar();
    A.node.p2 = new bar();
    ptr = &A.node;

    struct foo *n = ((struct foo*)((char*)(ptr)-(unsigned long)(&((struct foo*)0)->node)));
    cout << n->a << endl;

    return 0;
}

代码用g++ -Wall -Werror -pedantic -std=c++14 test.cpp. 问题是我认为您没有为p1and分配内存p2

于 2017-03-17T14:44:05.810 回答