天天看點

c++ 由類型轉換引起的指針偏移

由于轉載了另外的轉載,且原文暫時未找到,此處無法列出原文位址。

在C語言中,如果對一個指針做類型轉換,不會改變這個指針的值,改變的隻是對指針的解釋方式。但是在C++中,由于一些特性的引入,在對指針做類型轉換時,編譯器有時不得不對指針做一個偏移,以支援這些特性。下面将具體讨論這些情況。

1. 由虛函數引起的指針偏移

通常在有虛函數的類中,編譯器會安插一個vptr,但是對vptr的位置C++語言并未做出明确的規定,就目前的實作來看,有的編譯器将vptr放在類的開頭,如microsoft的c++編譯器,而有的編譯器将vptr放在類的末尾,如cfront。

對于将vptr放在類的開頭這種編譯器,我們考慮如下兩個類:

X的記憶體布局很簡單,由于它沒有虛函數,是以沒有vptr,隻有一個int類型的變量i;

而Y的記憶體布局可能像這樣:

vptr

 int i

 int j

對于下面這樣的代碼:

Y y;

X* pX = &y;

編譯器為了保證将Y類型的指針轉換為X類型的指針後,指向的是一個有效的X對象,它将不得不做一個偏移,實際的代碼可能像這樣:

X* pX = (X*)((char*)&y + sizeof(vptr))

2. 由多重繼承引起的指針偏移

首先需要說明一下多重繼承的記憶體布局,雖然C++語言未明确做出規定,但目前的多數實作會按照基類聲明的順序依次将每個基類的資料成員順次放入派生類。例如:

C的記憶體布局可能像這樣:

 int k

有了這樣的布局,很容易就能猜到編譯器在做類型轉換時對指針做出的偏移,例如

C c;

B* pb = &c;

猜想實際的代碼可能是:

B* pb = (B*)((char*)&c + sizeof(A));

對于例子中的情況,上面的猜想是成立的,但對于一般的情況就不成立了。在将C類型的指針轉換的時候,這個C類型的指針有可能為空,按上面的轉換思路,那空指針也得加一個偏移量了,為了避免這種情況,實際的代碼可能會是這樣:

B* pb = (pc) ? (B*)((char*)pc + sizeof(A)) : 0; //假設pc是一個指向C的指針

3.由虛繼承引起的指針偏移

對 于一般的繼承(這裡僅指單繼承),在不涉及虛函數的情況下,由于編譯器的實作多會将基類資料成員排在派生類之前,是以從派生類指針轉換為基類指針時,一般 不需要做偏移。但是對于虛繼承,情況就不一樣了。由于虛基類的位址是在運作時确定的,是以由派生類的指針轉換為虛基類的指針時必然會引起偏移。虛基類有多

種實作方式,是以在這兒就先不舉例了。可以參考<Inside the C++ Object Model>.

繼續閱讀