2011-07-28 44 views
15

我注意到,不允許創建零堆長的非堆分配數組。爲什麼零長度數組只允許分配堆?

// error: cannot allocate an array of constant length zero 
char a[0]; 

我還注意到它允許創建零長度的堆分配數組。

// this is okay though 
char *pa = new char[0]; 

我猜他們都是由標準保證的(我沒有標準副本)。如果是這樣,他們爲什麼如此不同?爲什麼不只是允許堆棧中的零長度數組(反之亦然)呢?

+6

堆上的零長度數組是一個太小而不適合放入任何東西的瓶子。堆棧上的零長度數組就像坦克中兩加侖水之間的空間...... :) – antlersoft

回答

5

0長度數組不是很有用。在計算 尺寸時,可能會發生這種情況,因此在您的代碼中不必特意處理案例 。在new之外,數組 的維數必須是常數,而不是計算值,如果知道常數爲0,爲什麼要定義它?

至少,這是我聽說過的理由(來自工作於 的人)。我並不完全相信:在代碼中,通常不需要 就可以使用其值不知的符號常量(來自標頭 文件,甚至命令行)。所以這可能是有意義的 允許0元素的數組。至少有一個編譯器在過去有 允許它們,但我已經忘記了哪個。

在C++中一個常見的技巧是使用這樣的陣列中的編譯時間 斷言,這樣的:

char dummyToTestSomeSpecificCondition[ condition ]; 

這將無法編譯,如果條件是假的,如果 它不是會編譯「T。除了那一個編譯器(如果它仍然存在);我將使用 類似:

char dummyToTestSomeSpecificCondition[ 2 * condition - 1 ]; 

,以防萬一。

15

這在C++標準的以下部分中得到了解決。

3.7.3.1/2:

[32。意圖是通過調用malloc()calloc()來實現運營商new(),因此規則基本相同。 C++選自C的不同之處需要零請求返回一個非空指針。]

並且還

5.3.4,第7段

當表達式中的值一個直接新的聲明符爲零,調用分配函數來分配一個沒有元素的數組。

8.3.4/1:


大小爲0的數組不是由C++標準允許

「如果_constant-expression+(5.19)時,它應是一個積分常數表達式,其值應大於零。「

根據我的理解,這背後的基本原理似乎是C++標準要求每個對象都必須有一個唯一的地址(這就是甚至空類對象的大小爲1的原因)。在一個非堆零大小的數組,不需要創建任何對象,因此不需要給它地址,因此不需要首先放置它。


就爲c而言,零個長度數組是allowed by the c standard,通常它們被用來通過將零長度陣列在結構的端部以實現具有可變尺寸的結構。如果我的記憶服務我的正確,它通常被稱爲C struct Hack

+0

我的主要問題實際上是,爲什麼他們在上面兩種情況下有所不同? –

+0

@Eric:請檢查更新後的答案。它試圖回答你的問題。心連心。 –

1

這取決於編譯器實現/標誌。 Visual C++不允許,GCC允許(不知道如何禁用)。使用這種方法 ,STATIC_ASSERT可以在VC來實現,而不是在GCC:

#define STATIC_ASSERT(_cond) { char __dummy[_cond];} 
+0

靜態聲明通常通過使用負數來實現。 Visual C++特別允許零大小的數組,儘管警告是「非標準擴展」。 – EboMike

+1

不可以。VC++不允許零大小的本地數組。 – Ajay

-1

即使直覺這是有道理的。

由於堆分配方法會在堆棧上創建一個指針,以便在堆上分配一塊內存,它仍然會「創建大小」:sizeof(void*)。在分配零長度數組的情況下,堆棧中存在的指針可以指向任何地方,但無意義。

相比之下,如果您在堆棧上分配數組,那麼有意義的零長度數組對象是什麼樣的?它沒有任何意義。

+0

這沒有意義。堆棧中沒有指針,'malloc'或'new'返回的指針與分配的內存無關。 –

+0

@James:那麼誰擁有的地址內存區域是由'new'或'malloc'分配的? –

+0

@J T叫他們的客戶。 (或者沒有人,在內存泄漏的情況下。) –

4

我認爲,爲什麼他們的行爲不同的主要原因是,第一個:

char a[0]; 

是大小爲0的數組,這是被禁止的,因爲它的規模應該是定義0 *的sizeof(字符),以及在C或C++不能定義大小爲0

的類型,但第二個:

char *pa = new char[0]; 

一個真正的數組,它只是一塊0類型的對象,其類型爲char將所有內容放在一起。由於0個對象的序列可能有用,所以這是允許的。它只是返回一個指針,超過最後一個項目,這非常好。

要添加到我的論點,考慮下面的例子:

new int[0][3]; //ok: create 0 arrays of 3 integers 
new int[3][0]; //error: create 3 arrays of 0 integers 

儘管這兩條線將ALLOC相同的內存(0字節),一個是允許的,另一種是不。