【问题标题】:pointer to struct or class versus pointer to first field指向结构或类的指针与指向第一个字段的指针
【发布时间】:2012-11-20 23:40:53
【问题描述】:

我最近尝试通过将几个指针的值打印到控制台来调试一个小程序。第一个是结构的内存地址,其他是其字段的内存地址。精简版代码如下:

#include <iostream>

struct testingPointers
{
    int i;
    float f;
    double d;
} test;

int main()
{
   std::cout << &test << '\n' << &(test.i) << '\n' << 
            &(test.f) << '\n' << &(test.d);
}

输出是:

0x681110
0x681110
0x681114
0x681118

(显然,不同运行的确切值不同,但它们之间的相对位置始终相同)。

我很困惑,因为第一个指针的值——test 的内存位置——与第二个指针(test 的第一个字段)的值相同。这是否意味着对象没有真正唯一的内存地址,并且指向结构或类的指针只是指向其第一个字段?如果是这样,像

这样的语句如何
a.b
a->b
a.b()

如果a 实际上只是它的第一个字段,因此没有任何字段或方法,是否有意义?

【问题讨论】:

  • 不,它们是不同类型的指针。但是第一个字段位于对象的开头。所以它具有相同的起始地址。 (除非开头有 vtable 指针之类的。)
  • 如果一个指向我自己头顶的指针也是一个指向我头顶的指针,这是否意味着我不存在,因为我真的只是一个头?

标签: c++ oop pointers struct field


【解决方案1】:

对象的地址应始终是该对象中第一个非静态成员的地址。引用标准(C++11-9.2-20):

指向标准布局结构对象的指针,使用 reinterpret_cast 适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然. [注意:因此,标准布局结构对象中可能存在未命名的填充,但不是在其开头,这是实现适当对齐所必需的。

这里提到了标准布局的要求:StandardLayoutType

这当然可以通过嵌套来应用。该标准对位域的第一个成员 excepttype 没有任何例外。即:

class X
{
public:
    int x;
};

class Y
{
public:
    X x;
    int y;
};

Y yobj;

按照标准,&amp;yobj == &amp;yobj.x == &amp;yobj.x.x

【讨论】:

  • +1.. 我正在寻找这个“一个指向标准布局结构对象的指针,使用 reinterpret_cast 进行适当转换,指向它的初始成员”。非常感谢。
【解决方案2】:

一个类或结构只是描述了一个字段的集合,这些字段应该一起保存在内存中,并且在它们之间有一些语义关系以及对它们进行操作的一些操作。在简单的情况下,内存中类类型对象的内容只不过是由它组成的成员(和一些填充)。当内存中有一个testingPointers 对象时,它实际上只是一个int、一个float 和一个double。类的概念仅用于生成正确的可执行代码——它在运行时不存在(至少不用于此目的)。

标准中关于对象是否可以共享内存地址的重要部分是§1.8/6:

除非对象是位域或大小为零的基类子对象,否则该对象的地址就是它占用的第一个字节的地址。如果一个对象是另一个对象的子对象,或者如果至少一个是大小为零的基类子对象并且它们属于不同类型,则两个不是位域的对象可能具有相同的地址;否则,它们将具有不同的地址。

由此我们可以推断,因为成员test.itest的子对象,所以他们很可能有相同的地址。

一旦您尽可能深入地查看程序的所有对象,您真正拥有的是大量标量值和相邻位域。这些在标准中称为内存位置。这些是真正占用空间的东西。您的其余对象都以某种方式由这些组成。

内存位置要么是标量类型的对象,要么是所有具有非零宽度的相邻位域的最大序列。 [ 注意: 语言的各种特性,例如引用和虚函数,可能涉及程序无法访问但由实现管理的额外内存位置。 — 尾注 ]

【讨论】:

    【解决方案3】:

    只是为了澄清你的困惑:

    1).第一个指针的值--test的内存位置--与第二个(test的第一个字段)的值相同。
    Struct 的地址和它的第一个字段必须相同,因为 struct 不是它的字段的连续集合。

    你也可以考虑数组的情况来进一步简化理解,其中数组的地址显然会等于数组的第一个元素(字段)。

    2)。这是否意味着对象没有真正唯一的内存地址,而指向结构或类的指针只是指向其第一个字段?
    我认为您将它与 JAVA 混淆了,其中对象总是在堆上分配。在 C++ 中,结构/类总是在堆栈上分配,除非它是动态内存分配(使用“new”运算符),其中对象在堆中分配,并且指针变量(在堆栈中)将指向堆中的该对象。因此,在前一种情况下,struct 变量的地址总是与其第一个元素(字段)的地址相同。

    希望对您有所帮助。

    【讨论】:

    • 啊,是的,我可能在考虑 Java。而且数组类比很有帮助。
    【解决方案4】:

    内存中的结构仅由串在一起的字段组成。根据对齐需要,结构之前和/或字段之间可能有填充,但在第一个字段之前通常没有任何额外的“东西”。所以第一个字段和结构本身具有相同的地址。

    请记住,在 C 中,类型仅在编译时存在。

    【讨论】:

    • 我从未听说过将填充放在第一个成员之前的编译器,尽管我不确定它在 C 中是否允许。我认为它允许的(尽管仍然从未见过)在 C++ 中。
    • @MooingDuck 不符合标准 (9.2-20)。在这篇文章的某个地方看到我的答案。开头不允许
    猜你喜欢
    • 1970-01-01
    • 2011-08-04
    • 1970-01-01
    • 2018-05-16
    • 2016-03-21
    • 2012-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多