C Data Member記憶體布局

2021-09-08 09:37:02 字數 2841 閱讀 1677

如果乙個類只定義了類名,沒定義任何方法和字段,如class a{};那麼class a的每個例項占用1個位元組的記憶體,編譯器會會在這個其實例中安插乙個char,以保證每個a例項在記憶體中有唯一的位址,如a a,b;&a!=&b。如果乙個直接或是間接的繼承(不是虛繼承)了多個類,如果這個類及其父類像a一樣沒有方法沒有字段,那麼這個類的每個例項的大小都是1位元組,如果有虛繼承,那就不是1位元組了,每虛繼承乙個類,這個類的例項就會多乙個指向被虛繼承父類的指標。還有一點值得說明的就是像a這樣的類,編譯器不一定會產生傳說中的那6個方法,這些方法只會在需要的時候產生,如class  a沒有被任何地方使用那這些方法編譯器就沒有必要產生,如果這個類例項化了,那麼會產生default constructor,而destructor則不一定產生。

如果乙個類中有static data member,nonstatic data member,還有const data member,enum,那麼它的記憶體布局會是什麼樣的呢,看下面簡單的類point:

class

point

private

:

intx;

static

intcount;

inty;

const

intmaxcount ;

enum

;};

sizeof(point)=12,為什麼佔12位元組呢,我相信很多人都知道是哪幾個成員變數占用的,就是x,y,maxcount,maxcount作為常量字段,但在point的每個例項中可能有不同的值,當然屬於point例項的一部分,如果把maxcount定義成static,那它就不不是point例項的一部分了,如果定義成static  const int maxcount=1;則maxcount分配在.data段中,如果沒有初始化則分配在.bss段中,反正跟point的例項無關,count分配在.bss段中,mincount分配在.rdata段中,總之count,maxcount,mincount在編譯連線完成之後,記憶體(虛擬位址)就分配好了,在程式載入的時候,會把他們的虛擬位址對應上實際的實體地址。

data member的記憶體布局:nonstatic data member在class object中的順序和其申明的順序一樣,static data  member和const member不在class object中因為他們只有乙份,被class object共享,所以static data member和const data member,列舉並不會響應class object的大小。關於段的資訊,我覺得是每個c/c++程式設計師必須知道的。而point每次例項化的時候則只需要分配x,y,maxcount需要的記憶體。

每個類的data member在記憶體中應該是連續的,如果出現資料對齊的情況,可能中間會有空白地帶。請看下面幾個類:

classaa;

class bb:publicaa;

class cc:public

bb;

sizeof(aa)=8//對齊3位元組

sizeof(bb)=12//兩個3位元組對齊

sizeof(cc)=16//編譯器「無恥」的用了3個3位元組對齊

關於記憶體對齊問題我相信大家經常遇到,我就不廢話,我之前寫過一篇關於記憶體對齊的文章《資料對齊》

編譯器為什麼要無恥的在class cc中加3個3位元組對齊呢,這樣每個cc的例項就大了9位元組。如果編譯器不加這9位元組的空白,那麼cc的每個例項就是8位元組,前面的x佔4位元組,後面的a,b,c佔3位元組,加1位元組的空白對齊,剛好8位元組,沒有誰很傻很天真的以為最好是佔7位元組吧。

如果cc占用8位元組記憶體,同樣的aa,bb都是8位元組的記憶體,這樣的話,如果把乙個指向aa例項的指標賦給乙個指向cc例項的指標,那麼就會把aa中的8位元組直接蓋到cc的8位元組上,結果cc例項中的b,c都被賦上了不是我們想要的值,這很可能會導致你的程式出問題。

父類的data member會在子類的例項中有完整的乙份,這樣在有繼承關係的類之間進行型別轉換,就只用簡單的修改指標的指向。

data member的訪問。對乙個data member的訪問,編譯器把物件例項的起始位址加上data member的偏移量。如cc c;

c.x=1;相當於&c+(&cc::x-1),減一其實是為了區分是指向object的指標還是指向data member的指標,指向data member的要減一。每乙個data member的偏移量在編譯的時候是知道的,根據成員變數的型別和記憶體對齊,存在virtual繼承或是虛方法的情況編譯器會自動加上一些輔助的指標,如指向虛方法的指標,指向虛繼承父類的指標等。

在data member的訪問效率上,struct member 、class member、單一繼承或是多重繼承的情況下效率都是一樣的,因為他們的儲存其實都是&obj+(&class.datamember-1)。在虛繼承的情況下,可能會影響儲存效能,如通過乙個指標來訪問乙個指向虛繼承而來的data member,那麼效能會有影響,因為在虛繼承的時候,在編譯的時候還不能確定這個data member是來自子類還是父類,只有在執行的時候才能推斷出來,其實就是多了一步指標的操作,在虛繼承中,如果是通過物件例項來操作虛繼承而來的data member,則不會有任何效能問題,因為不存在什麼多型性,所有東西在編譯的時候記憶體位址都確定了。

虛繼承還是虛方法為了實現多型一樣,多了一步,如果不需要多型,而是通過物件例項呼叫相關的方法就不會有效能問題。

C Data Member記憶體布局

如果乙個類只定義了類名,沒定義任何方法和字段,如class a 那麼class a的每個例項占用1個位元組的記憶體,編譯器會會在這個其實例中安插乙個char,以保證每個a例項在記憶體中有唯一的位址,如a a,b a b。如果乙個直接或是間接的繼承 不是虛繼承 了多個類,如果這個類及其父類像a一樣沒有...

struct記憶體布局

結構體的記憶體分配原則原則1 資料成員對齊規則 結構 struct或聯合union 的資料成員,第乙個資料成員放在offset為0的地方,以後每個資料成員儲存的起始位置要從該成員大小的整數倍開始 比如int在32位機為4位元組,則要從4的整數倍位址開始儲存 原則2 結構體作為成員 如果乙個結構裡有某...

Objective C記憶體布局

第2章 c變數 當用大多數常見的指令碼程式語言編寫乙個程式時,幾乎不必花時間來考慮變數。只是在使用變數時才建立它們,並且不必擔心用完它們之後會發生些什麼。語言的直譯器會負責所有的細節。當你在編譯語言中編寫 時,事情就沒那麼簡單了。必須告訴編譯器每個變數的型別和名稱,以宣告任何將要在程式中使用的變數。...