【问题标题】:memory allocation and 0 size: can I get the memory leaks?内存分配和 0 大小:我可以得到内存泄漏吗?
【发布时间】:2013-06-29 11:16:07
【问题描述】:

我的问题位于我的代码注释中:

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?

谢谢。

【问题讨论】:

  • @Kolyunya 通常没有人会,但是如果大小是一个可以为零的变量呢?
  • 这个问题的实际方面也很有趣。分配 0 字节有什么实际用途吗?还是只是一个概念问题?
  • 是的,如果你调用 malloc(0),你会得到一个有效的指针(你显然不应该尝试写入)。你确实需要“释放”它。新建/删除也是如此。
  • @selbie 调用malloc(0) 可能会返回一个可以传递给free 的有效指针,也可能返回NULL。它是实现定义的。
  • @Kolyunya 我的意思是如果你有一个变量的大小,它来自某个地方(文件、用户、数据库等)并且你使用该变量进行分配。如果您不必为零大小添加特殊情况,代码很可能会更容易编写。

标签: c++ memory-leaks new-operator


【解决方案1】:

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

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

【讨论】:

  • 怎么会是内存泄漏?会泄漏多少内存?
  • @Kolyunya 我想这取决于内存分配器。
  • 此外,分配器必须为每个new int[0] 请求返回一个新指针,并且这些指针在所有分配中必须是唯一的。很明显,在一些new后面没有delete之后,指针就会用完
【解决方案2】:

在这种情况下,实现定义是否会返回nullptr,但你应该小心,不要取消引用这个指针,也不要调用delete 将导致内存泄漏。

W.r.t 打电话给delete 规则很简单:
“如果你打电话给new,你必须打电话给delete。”

更正:
正如其他答案中的引文已经清楚地表明,它不能给你一个nullptr

【讨论】:

  • 示例中不调用delete会怎样?会泄漏多少内存?
  • @Kolyunya:如果你不调用delete,分配的任何内存(取决于实现)都会泄露。唯一可以确定的是它将是非零的。
  • 我曾经认为如果您分配0 字节,则没有什么可删除的...谢谢您的信息。
  • 它不依赖于实现。 new T[0] 不能产生 nullptr
  • @Kolyunya 内存分配器通常必须分配额外的簿记结构来存储您的堆块,因此即使是 0 字节块也会有一些开销。通常,每个堆块的开销约为 8-16 个字节,但它可能会有所不同(并且取决于实现)。
【解决方案3】:

是的,存在泄漏,并且与实现无关。

这个新表达式不能产生空指针。它通过调用operator new[] 分配内存,这是“将非空指针返回到适当对齐的存储空间,否则抛出bad_alloc 异常”所必需的(参见C++11 §18.6.1.1/3 和§18.6。 1.2/3)。

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

这样,每个数组形式的新表达式都会分配一些东西,即使范围为零。如果你不通过delete[] 释放该对象,你已经泄露了它。

【讨论】:

    【解决方案4】:

    在 C++03 中 new int[0] 导致未定义的行为,因为 [] 之间的值必须是严格的正值 - 零不好(5.3.4/6“新”)。所以问事后有没有内存泄漏在某种意义上是没有意义的。

    在 C++11 中,new int[0] 导致调用分配器以分配零长度数组(5.3.4/7“新”)。如果分配请求成功,则返回一个指针 - 标准中没有说明该指针指向的块包含多少内存,除了它必须至少是请求的大小。但是,它至少具有分配至少一个字符的效果,因为分配器在释放该地址之前不能再次返回该地址。实际上,记账开销会超过一个字符。

    【讨论】:

    • 哦;我不知道 C++03 和 C++11 之间的这种区别;很有意思。 C++03 确实包含语言“new-expression 产生一个指向数组的初始元素 (如果有) 的指针”(强调我的)。在 C++11 中阅读此内容时,我假设此文本用于处理计数为零的情况,但由于它存在于 C++03 中,也许它也用于处理非抛出形式返回 null 的情况?
    • 关于 C++03 的评论是完全错误的。在 C++03(第 5.3.4/6 节)中:“direct-new-declarator 中的表达式应具有非负值的整数或枚举类型 (3.9.1)。” 0 是非负值。 (事实上​​,自 C++98 以来,这里没有任何变化。)
    • “每一个 constant-expression ... 都应该 ... 计算为严格的正值”(标准强调)- 这涉及到 new float[n][5] 之类的代码(示例取自标准),其中 5 (constant-expression) 应该是正数(并在编译时评估),n (expression) 应该是非负数(并在运行时评估)
    【解决方案5】:

    对于 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 必需的行为:与运算符 new(std::size_t) 相同。此要求对替换具有约束力 此函数的版本。

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

    ...引用 18.6.1.1...

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

    3 必需的行为:返回一个 non-null 指向适当对齐的存储 (3.7.4) 的指针,否则抛出一个 bad_- 分配异常。此要求对该函数的替换版本具有约束力。

    所以,返回的指针一定是非空的。

    之后你确实需要delete[]它。

    【讨论】:

    • Bjarne Stroustrup 回答了我:operator new 总是返回一个指向对象的指针(如果内存耗尽,它会抛出 bad_alloc)。如果您不使用删除新的,您会得到泄漏。确切的内存泄漏量是特定于实现的,可能是两三个字。
    • @Bush:好吧,老实说,我觉得有点难过,你花了他的时间去做一些你已经被告知的事情,但是你已经知道了......
    • 不,在您的答案被标记为“答案”后,我得到了 B. Stroustrup 的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-20
    • 2016-09-12
    • 2019-08-12
    • 1970-01-01
    • 2011-08-17
    • 2017-04-08
    • 1970-01-01
    相关资源
    最近更新 更多