指向struct或class的指针与指向first字段的指针

时间:2012-11-20 23:40:54

标签: c++ oop pointers struct field

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

#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实际上只是它的第一个字段,那么

是否有意义,因此没有任何字段或方法?

4 个答案:

答案 0 :(得分:6)

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

  

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

此处提到了标准布局的要求:StandardLayoutType

这当然可以通过嵌套来应用。对于位字段的第一个成员之外的类型,该标准没有例外。即:

class X
{
public:
    int x;
};

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

Y yobj;

按标准&yobj == &yobj.x == &yobj.x.x

答案 1 :(得分:5)

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

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

  

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

我们可以从中推断,因为成员test.itest的子对象,所以它们可能具有相同的地址。

一旦你深入了解程序中的所有对象,你真正拥有的是标量值和相邻位域的大集合。这些在标准中称为内存位置。这些是真正占据空间的东西。其余的对象都以某种方式由这些组成。

  

内存位置是标量类型的对象或相邻位域的最大序列,所有这些都具有非零宽度。 [注意:该语言的各种功能(如引用和虚函数)可能涉及程序无法访问但由实现管理的其他内存位置。 - 结束记录]

答案 2 :(得分:3)

只是为了澄清你的困惑:

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

enter image description here

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

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

希望有所帮助。

答案 3 :(得分:1)

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

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