2011-07-28 38 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字节),一个是允许的,另一种是不。