為什麼一個空的class的大小是1個位元組?
為何如此呢?按照書的說法,是編譯器安插了一個1 byte 的char, 用於賦予不同X instance的地址。這點我完全不能理解。。。按照這個說法,那麼就一共可以生成128個不同的X instance?這不是扯淡嗎。還是,為了allocate memory,必須存有一個毫無意義的數據?
在c++11下,這樣的現象還存在嗎?
你理解錯了,不是用那一個char來區分不同的X實例。為每一個X實例分配1位元組空間,這樣就可以讓不同的X實例地址不同了,以此區分不同的X實例。如果大小為0,那麼很多個X實例放在一起的時候他們的地址是相同的,無法分辨。
隨便反對一下大小為0就沒有地址的說法。申請大小為0的內存空間是沒有問題的,也會被分配一個地址。你想像一下如果創建一個空類的數組,那怎麼讓每個元素有不重複的地址呢?
X* myarray = new X[N];
==============================================
@Misaka Mikoto 你可能有點誤解,分配空間大小為0的地址段沒啥問題,但是數組的所有元素指向同一個地址就有點要命了,你想一下使用iterator循環for(X* i = myarray[0]; i != myarray[N]; ++i){
...
}
這個循環是STL當中許多通用演算法的基礎,而如果元素大小為0,那麼myarray[0]和myarray[N]地址就相等了,這個循環直接就進不去了。
在你的回答下面評論的是另一個問題,就是new int[0]這樣的語句分配到的究竟是怎樣的內存段的問題。new底層是用malloc實現的,首先從實現上來說,無論如何都要真實分配出去至少一位元組,來把這個內存占著,否則以後某一次malloc的時候就會把這個地址再分配出去一次,那就糟糕了,當你使用free(XXX)的時候,會把錯誤的內存釋放掉。
其次是new int[0],C++分配數組的時候,實際分配的內存大小通常會比這麼多元素占的空間要多一點,用來存儲分配數組的大小,因為按照規定:delete[] myptr;
的時候,會依次對數組中所有的元素調用析構函數,那麼就必須知道這個數組中元素的數量。所以C++當中new object和new object[]是不同的,也有兩種對應的delete object和delete[] object的語法。
一種實現當中大致是這樣的,假設分配4個位元組來存儲數組中元素數量:- 首先調用malloc,指定大小是sizeof(X) * N + 4。假設返回了0x00aa0000
- 在返回地址的開頭4個位元組填入數組元素個數,也就是0x00aa0000 - 0x00aa0003當中,填入N
- 將malloc返回的地址+4個位元組偏移,然後返回,也就是返回0x00aa0004
這樣即便是new int[0],也會額外分配一些位元組來存儲元素數量(也就是0)。
實際的分配演算法其實要更複雜一些,要處理一些跟對齊有關的問題。
=================================================
試了一下x64版本的gcc,gcc只有在這個類有析構函數的時候,才會額外分配8個位元組存儲數組長度,否則就不存儲了。
class Empty{};
int main() 如果空類是0,你覺得這個數組和vector會怎樣
{
Empty arr[20];
std::vector&
}
順便說C標準不允許struct沒有成員
#define array_size(x) (sizeof(x)/sizeof((x)[0]))
很多地方都有這個代碼,如果空類型結構體為0,必然會導致很多代碼出錯。題目的描述不是很嚴謹,就好像計算機對於除數為 0 不能給確定結果一樣,就像對於資料庫中的 NULL 也是不確定和未知一樣,這個問題也因為有些無意義而無從爭論。此外,我想解釋的是(主觀臆測,因此編譯器開發者更有話語權),沒有任何信息需要存儲的對象實例,實現時會進行有實際內存分配,給予佔位意義的內存空間,這種做法的考慮。
sizeof (X) = 1 也許只是暗示編譯器行為,對象實例存在內存分配,對象實例會有明確地址。但是其對象所在內存空間的數據(就是這 1 byte)無實際意義(除了用於佔位)。
當然,其他朋友的評論中提到的是另一個問題,就是動態內存分配對於特殊參數(malloc(0))的考慮。這種情況下,返回的地址是無法訪問的(至少在邏輯上是不可以的)。然而 sizeof (X) = 1 的情況不是,因為它相當於 malloc (1) ,其內存是絕對可以訪問的。
--
class / struct 的實例可以作為函數參數,從調用方傳到函數,拷貝構造。例如 STL 中的 functor,例如 find_if 的參數。
如果 class / struct 沒有任何數據成員,也沒有虛函數,那它實際上不需要分配存儲空間。但是在這個情況下,實例作為參數時,依然會在棧上佔據一個基本粒度(例如 4 bytes)。當然,這塊內存的數據實際上並沒有人去動它。(當然,這裡我說的是有具體所指,也就是 VC 編譯器。)
至於為什麼,也許是為了實現時形式上的統一吧。因為構造對象實例時,需要調用構造函數,構造函數需要對象實例的被分配內存的起始地址。
如果對於這樣的對象實例,若不給它分配一個實際存在的地址,那麼在這裡就會需要特殊處理,也就是形式上就會不統一。
換句話說,對於class X { void foo() { }; };X x;要對 x 不分配內存,特殊對待嗎(還是讓他實際在內存中存在呢)?我想還是沒必要的。因為這樣只會讓你自己陷入更多麻煩吧。(為什麼佔1位元組很多人說了,不再贅述)佔1位元組就1位元組嘛,有什麼關係。捨不得多佔用內存?利用空基類優化,很多地方都可以讓空類不佔用額外空間。
template&
bool = std::is_empty&
class Pair: Left {
Right r;
public:
Left left() { return *this; }
Right right() { return this-&>r; }
};
template&
class Pair&
Left l;
public:
Left left() { return this-&>l; }
Right right() { return *this; }
};
template& 可以測試一下編譯器有沒有做空基類優化。
class Pair&
Left l;
Right r;
public:
Left left() { return this-&>l; }
Right right() { return this-&>r; }
};
struct Empty {};
auto empty_lambda = [](int x) {return x; };
static_assert(sizeof(Pair&
static_assert(sizeof(Pair&
實際只是用了1個位元組大小的空間而已,並沒有規定裡面放的是啥,為什麼這麼做呢,如果空 Class 的大小是0,那麼就是沒有為它分配任何內存,於是
X x;
ptrdiff_t ptr = x; // 這個 ptr 的值是多少呢?
0也不會有任何問題 swift空結構體大小就是01可能是歷史遺留的 因為考慮兼容性 所以也不會改了
使用數據的過程里,空struct佔不佔位元組是無所謂的,因為本來就沒有數據。
問題出在指針運算里。C/C++的指針除了單純的指向,還兼任了數組管理工具,而sizeof()==0以後我們就沒辦法判斷一個指針指向數組裡的哪個元素了,這會破壞很多代碼。推薦閱讀:
※C++非同步回調如何更優雅?
※[C++] 能否設計一個一般的計時函數?
※unity項目越大編譯速度越慢 ue4用藍圖秒編譯 背後分別的原因是什麼?
※c++的強制類型轉換?
※對於 計算機圖形/界面/可視化/遊戲畫面/遊戲引擎 的疑惑?