2012-11-20 215 views
6

我最近試圖通過將幾個指針的值打印到控制檯來調試一個小程序。第一個是結構的內存地址,其他的是其字段的內存地址。代碼的精簡版本如下:指向結構或類的指針與指向第一個字段的指針

#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實際上只是它的第一場,因此沒有任何字段或方法?

+2

不,它們是不同類型的指針。但是第一個字段位於對象的開始處。所以它有相同的起始地址。 (除非有一個vtable指針或開始的東西。) –

+8

如果一個指向我自己頂部的指針也是一個指向我頭頂的指針,這是否意味着我不存在,因爲我真的只是一個頭? –

+0

@MooingDuck令人毛骨悚然。 –

回答

5

一個類或結構只是描述了應該在內存中保存在一起的字段的集合,並且它們之間有一些語義關係以及對它們進行操作的一些操作。在簡單的情況下,在內存中的類型對象的內容比構成它的成員(以及一些填充)要多得多。當內存中有testingPointers對象時,它實際上只是一個int,floatdouble。該類的概念僅用於生成正確的可執行代碼 - 它在運行時不存在(至少不用於此目的)。

最重要的部分從關於對象是否能夠共享存儲器地址的標準是§1.8/ 6:

除非對象是一個位字段或大小爲零的基類子對象,的該地址object是它佔據的第一個字節的地址。如果一個是另一個的子對象,或者如果至少有一個是大小爲零的基類子對象,並且它們的類型不同,則兩個不是位域的對象可能具有相同的地址;否則,他們應該有不同的地址。

我們可以由此推斷,因爲成員test.itest子對象,他們可能也有相同的地址。

一旦你深入到你的程序的所有對象中去,你真正擁有的是標量值和相鄰位域的大集合。這些被稱爲的標準存儲位置。這些是真正佔據空間的東西。其餘的對象都是以某種方式組成的。

存儲器位置或者是標量類型的對象或相鄰位字段都具有非零寬度的最大序列。 [備註:該語言的各種功能(例如引用和虛擬功能)可能涉及附加的內存位置,這些位置對於程序而言是不可訪問的,但由實現來管理。 - 注完]

1

內存中的結構是由什麼東西比它的字段串在一起了。根據對齊的需要,可能會在結構之前和/或字段之間進行填充,但在第一個字段之前通常沒有任何額外的「內容」。所以第一個字段和結構本身具有相同的地址。

請記住,在C中,類型只在編譯時才存在。

+0

我從來沒有聽說過在第一個成員之前加入填充的編譯器,但我不確定它是否允許在C中使用。我認爲它在C++中是允許的(儘管仍然沒有看到)。 –

+0

@MooingDuck不符合標準(9.2-20)。看到我的答案浮在這篇文章的某處。它在開始時是*不允許的。 – WhozCraig

6

對象的地址應始終爲該對象內第一個非靜態成員的地址。從標準(C++ 11-9.2-20)報價:

指向一個標準佈局結構對象,使用的reinterpret_cast適當轉換,點到它的初始成員(或如果該構件是一個位字段,然後到它所在的單元),反之亦然。 [注意:因此可能會在標準佈局結構對象中存在未命名的填充,但不是在開始時根據需要實現適當的對齊。

標準佈局的要求是這裏提到:StandardLayoutType

這當然可以通過嵌套來應用。該標準對類型的第一個成員除了位字段外不例外。 I .: .:

class X 
{ 
public: 
    int x; 
}; 

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

Y yobj; 

由標準,&yobj == &yobj.x == &yobj.x.x

+0

+1 ..我正在尋找這個*「一個指向標準佈局結構對象的指針,使用reinterpret_cast適當地轉換,指向它的初始成員」*。非常感謝。 – Nawaz

3

只是爲了澄清您的混亂:

1)。價值第一指針的 - 測試的存儲器位置 - 是相同的,所述第二一個(試驗的第一個字段)的。
Struct的地址和它的第一個字段是相同的,因爲struct不是連續的字段集合。

enter image description here

還可以考慮陣列的情況下,以進一步簡化理解,其中所述陣列的地址顯然會等於陣列的第一個元素(字段)。

2).Does這意味着對象有沒有真正的唯一的存儲地址,以及一個指向結構或類僅指向它的第一場?
我認爲你把它與JAVA的混淆,其中對象總是分配在堆上。在C++中,struct/class總是在Stack上分配,除非它是在堆中分配對象的動態內存分配(使用'new'運算符),並且指針變量(在Stack中)將指向堆中的該對象。因此,在前一種情況下,結構變量將始終具有與其第一個元素(字段)相同的地址。

希望有所幫助。

+1

啊,是的,我可能正在思考Java。陣列比喻非常有幫助。 – ApproachingDarknessFish