内存分配和0大小:我可以得到内存泄漏吗?

14

我的问题在代码注释中:

int* a = new int[0];// I've expected the nullptr according to my logic...
bool is_nullptr = !a; // I got 'false'
delete[] a; // Will I get the memory leaks, if I comment this row?

谢谢。


3
通常情况下没有人会这样做,但如果尺寸是一个可为零的变量呢? - Some programmer dude
1
此外,问题的实际应用也很有趣。分配0字节有任何实际用途吗?还是只是一个概念性问题? - Kolyunya
4
是的,如果你调用malloc(0),你会得到一个有效的指针(显然不应该尝试对其进行写入)。你确实需要"free"它。对于new/delete同样适用。 - selbie
1
@selbie 调用 malloc(0) 可能会返回一个可以传递给 free 的有效指针,也可能返回 NULL。这是实现定义的。 - Some programmer dude
1
@Kolyunya 我的意思是,如果您有一个变量来表示大小,该变量来自某个地方(文件、用户、数据库等),并且您使用该变量进行分配。如果您不必为零大小添加特殊情况,则代码很可能会更容易编写。 - Some programmer dude
显示剩余7条评论
5个回答

16

对于C++11,鉴于您的代码:

int* a = new int[0];

根据5.3.4/7规定,零是一种合法的尺寸:

当noptr-new-declarator表达式的值为零时,调用分配函数来分配一个没有元素的数组。

按照18.6.1.2所述,调用的运算符如下(强调为本人添加):

void* operator new[](std::size_t size);

...

3 必要行为:与operator new(std::size_t)的要求相同。此要求对该函数的替代版本具有约束力。

4 默认行为:返回operator new(size)。

...参考18.6.1.1...

void* operator new(std::size_t size);

3 要求行为:返回一个适当对齐的存储器的非空指针(请参见 3.7.4),否则抛出 bad_alloc 异常。此要求对此函数的替代版本具有约束力。

因此,返回的指针必须是非空的。

您需要在使用完后delete[]它。


Bjarne Stroustrup回答我说:operator new总是返回一个指向对象的指针(如果内存不足则抛出bad_alloc异常)。如果你没有使用delete来释放new分配的内存,就会造成内存泄漏。泄漏的内存量取决于具体实现,可能是两个或三个字。 - Andrey Bushman
4
@Bush: 坦白说,我感到有点难过,因为你浪费了他的时间来做一件你已经被告知过的事情,但是没办法…… - Tony Delroy
1
我的问题已经被B. Stroustrup回答了,在你的回答被标记为“答案”之后。 - Andrey Bushman

10
在C++03中,new int[0]会导致未定义的行为,因为[]之间的值必须是严格正值 - 零是不好的(5.3.4/6 "New")。因此,在这之后询问是否存在内存泄漏在某种意义上是无意义的。
在C++11中,new int[0]会调用分配器来分配零长度数组(5.3.4/7 "New")。如果分配请求成功,则会返回一个指针 - 标准没有说明该指针所指向的块包含多少内存,除了它必须至少是请求的大小。然而,它至少具有分配至少一个字符的效果,因为该地址不能再次由分配器返回,直到它被释放。在实践中,簿记开销将超过一个字符。

1
哦,我不知道C++03和C++11之间有这个区别,非常有趣。C++03包括语言“_new-expression_返回数组的初始元素指针(如果有)”(重点是我的)。当在C++11中阅读这段文字时,我以为它是处理计数为零的情况,但既然它也存在于C++03中,也许它还用于处理非抛出形式返回null的情况? - James McNellis
4
关于C++03的评论完全不正确。在C++03(§5.3.4/6)中:“直接new声明符中的表达式应具有带非负值的整数或枚举类型(3.9.1)。” 0是一个非负值。(实际上,自C++98以来没有任何更改。) - James Kanze
1
每个常量表达式...都应该评估为严格正值(标准强调)。这是关于代码的讨论,例如从标准中提取的new float [n] [5],其中5常量表达式)应该是正数(并在编译时评估),而n表达式)应该是非负数(并在运行时评估)。 - anatolyg

7

是的,有一个泄漏问题,并且它不依赖于实现。

这个新表达式无法产生空指针。它通过调用operator new[]来分配内存,该函数必须“返回适当对齐的存储器的非空指针,否则抛出bad_alloc异常”(参见C++11 §18.6.1.1/3和§18.6.1.2/3)。

此外,分配函数的要求(§3.7.4.1)要求每次调用分配函数返回一个指针,该指针与已分配但尚未释放的所有其他指针都不同。因此,实现不能简单地具有单个“空分配”指针,它总是返回该指针。

因此,每个数组形式的new表达式都会分配某些东西,即使大小为零。如果您不通过delete[]释放该对象,则会导致泄漏。


4

是的,如果没有delete,就会出现内存泄漏。

每个new都必须与delete配对使用。即使程序员分配的大小为0,分配器也可能分配比请求更多的内存,因为需要对齐、管理开销或其他原因。


它将如何成为内存泄漏?会泄漏多少内存? - Kolyunya
3
@Kolyunya 我想这取决于内存分配器。 - David Brown
此外,分配器必须为每个 new int[0] 请求返回一个新指针,并且这些指针在所有分配中必须是唯一的。很明显,在一些未跟随 deletenew 操作之后,将会用尽指针。 - user4815162342

4
在这种情况下,返回nullptr还是不返回由实现定义,但你需要小心,不要对指针进行解引用,同时不调用delete会导致内存泄漏。
关于调用delete的规则很简单:如果你调用了new,就必须调用delete
修正:正如其他答案中所引用的,它不可能返回nullptr

如果在这个例子中你不调用 delete,会发生什么?会泄漏多少内存? - Kolyunya
1
@Kolyunya:如果您不调用delete,则分配的任何内存(取决于实现)都将泄漏。唯一确定的是它将是非零的。 - Alok Save
我曾经认为如果你分配了 0 字节就没有什么需要删除的...谢谢你提供的信息。 - Kolyunya
2
它不依赖于实现。new T[0] 不能产生 nullptr - James McNellis
1
@Kolyunya 内存分配器通常需要分配额外的簿记结构来存储堆块,因此即使是0字节的块也会有一些开销。通常每个堆块的开销约为8-16字节,但它可能会有所变化(并且取决于实现)。 - Thomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接