【问题标题】:Why this program doesn't consume memory?为什么这个程序不消耗内存?
【发布时间】:2011-05-21 20:03:40
【问题描述】:

delete [] 实际做了什么让我很困扰,所以我只是尝试了一些代码,结果让我感到震惊

测试#1:

int main()
{
    int *d;
    while(true)
    {
        d = new int[10];
        delete []d;
    }
}

正如预期的那样,该程序根本不消耗任何内存。

测试#2:

int main()
{
    int *d;
    while(true)
    {
        d = new int[10];
        delete [](d + 5);
    }
}

虽然在每个循环中应该至少有 20 个字节(对于它在数组开头保留的五个整数)保留没有被删除,但这个程序也不会消耗任何内存!

测试#3:

int main()
{
    int *d;
    while(true)
    {
        d = new int[10];
        delete []d;
        *d=1;
    }
}

这如预期的那样导致了访问冲突(似乎在delete []d之后所有的内存都被删除了)。

测试#4:

int main()
{
    int *d;
    while(true)
    {
        d = new int[10];
        delete [](d+5);
        *d=1;
    }
}

这是最神奇的,虽然 while 不消耗任何内存,程序也不会产生任何访问冲突,我只是想知道 *d 将它的数据存储在哪里?

(顺便说一下,所有程序都是使用无优化编译的!)

现在主要问题:

如果我分配了一个数组并且我已经完成了其中一半的工作,我就不能释放那一半并保留另一半吗?

【问题讨论】:

  • 除了第一个之外,所有这些都具有未定义的行为。你不能删除任何不是新的东西。 d + 5 不是新的,d 是。无论如何,您如何确定它是否正在消耗内存?例如,我怀疑它是以千字节为单位报告的,而您分配的不超过千字节,所以它保持不变。
  • 如何知道程序是否消耗内存?
  • 只需尝试从它们中删除删除,您会发现消耗我所有 4GB 内存的时间不到 1 秒!
  • WTF?!您只能删除您已新建的内容。其他一切都是极其未定义的行为。
  • @Paul:指针消失了,当然,但不是动态分配的内存。

标签: c++ arrays delete-operator


【解决方案1】:

delete [] 困扰着我 确实如此

你不应该担心delete[] 实际做了什么。出于所有意图和目的,它是一个黑匣子,其中包含有关如何正确使用它的某些规则。您唯一需要担心它实际需要做什么的时候是您正在编写编译器或 C++ 运行时(例如操作系统等)

关于那些“如何正确使用它的某些规则”,测试#2 和#4 调用未定义的行为:

ISO C++ 2003 标准 5.3.5 删除 [expr.delete]

1 delete-expression 操作符会破坏由 new-expression 创建的最派生对象 (1.8) 或数组。

    delete-expression:
        ::opt delete cast-expression
        ::opt delete [ ] cast-expression

第一种选择是非数组 对象,第二个是数组。 操作数应具有指针类型, 或具有单个的类类型 将函数 (12.3.2) 转换为 指针类型。结果有类型 无效。

2 如果操作数具有类类型,则 操作数转换为指针类型 通过调用上述 转换函数和转换后的 操作数用于代替 剩余部分的原始操作数 本节。在任一种选择中, 如果删除操作数的值 是操作具有的空指针 没有效果。在第一种选择中 (删除对象),的值 delete 的操作数应该是一个指针 指向非数组对象或指向的指针 表示基础的子对象 (1.8) 此类对象的类别(第 10 条)。 如果不是,则行为未定义。 在第二种选择(删除数组)中,操作数的值 delete 应该是指针值 这是由先前的数组产生的 新表达。如果没有,则 行为未定义。 [注意:这个 意味着 delete-expression 必须与类型匹配 由new分配的对象,而不是 新表达式的语法。 ]

“未定义行为”表示任何事情都可能发生,包括您刚才描述的行为。

您在测试 #2 和 #4 中的这些表达式违反了 5.3.5/2,并且会导致未定义的行为(测试 #3 也会导致未定义的行为,但原因不同)。

d = new int[10];
delete [](d + 5);

delete[] 行违反了 5.3.5/2,因为您传递给 delete[] 的指针值与从 new int[] 给您的值不同。

所以如果new int[] 命令给你0xA01D2CE9 并且你将0xA01D2CE9 + 5 传递给delete[],你不可能推理或预测会发生什么,因为你已经违反了语言规则。实际会发生什么取决于编译器和/或操作系统如何处理new[]delete[]。范围从没有发生任何错误到系统完全崩溃,以及介于两者之间的任何地方。

换句话说,就是不要写delete [](d + 5);之类的东西。

【讨论】:

    【解决方案2】:

    当您说“不消耗任何内存”时,您指的是在“任务管理器”样式的性能监视器内部查看时?因为如果是这样,40 字节将不会显示为“内存使用情况”......您将不得不分配更多的内存才能显示在大多数标准进程性能监视器中。

    【讨论】:

    • 只要看一下我的程序,你就可以看到它是无限循环,所以每次迭代都需要 40KB,这真的是很多内存!用不到 1 秒的时间来使用我所有 4GB 的内存!
    • 如果这就是测试 4 不崩溃的原因是什么导致测试 3 崩溃?据我所知,操作系统会检查是否有任何程序只更改它保留的内存片段并违反这导致访问冲突!
    • 每次迭代不是 40KB,40 字节。
    【解决方案3】:
    delete [](d + 5);
    

    (来自您的测试#2)绝对是您应该做的事情,如果不是段错误,这将破坏内存。它实际上在我的平台上存在段错误。

    您的测试结果将取决于很多因素,其中包括您运行这些测试的平台、new/delete 的内部结构等。

    【讨论】:

      【解决方案4】:
      delete [](d + 5);
      

      听起来您希望这只会释放new int[10] 分配的部分内存。

      情况并非如此,它会导致未定义的行为并可能导致任何事情发生。

      在某些内存管理器上,如果您要求他们通过传递指针来释放块,如果您不传递指向块开头的指针,那么它们可能会释放 包含的整个块 您传递的指针。这很可能发生在您的案例中。

      另一个考虑因素是new int[10] 不会初始化分配的内存,因此操作系统可以只分配一些地址空间,而无需使用任何物理存储来支持分配。这意味着即使您在没有任何delete[] 的循环中调用new int[10],您也可能在许多内存监控工具中看到内存使用量没有上升,甚至当您耗尽new[] 抛出std::bad_alloc 异常时逻辑bvg地址空间。 (每次仅分配 10 个字节可能需要一些时间。)

      【讨论】:

        【解决方案5】:

        您是否在发布版本下运行这些测试?你看过生成的汇编指令吗?你如何测量内存消耗?您只需分配 40 个字节,然后立即清除它。

        此外,执行delete [](d + 5) 之类的操作会调用未定义的行为。您需要传入operator new 返回的指针,delete 才能正常工作。

        【讨论】:

        • 主要问题是,如果我通知了一个数组并且我已经完成了其中一半的工作,我不能通过任何方式释放我已经占用的内容吗?
        • 并告诉你我没有使用任何优化!
        • 不,您不能“释放”您使用 new 运算符分配的数组的一部分。正如其他人所提到的,这样做会导致未定义的行为。您也不能使用 C 风格的 malloc()free() 来做到这一点,因为 malloc() 返回的指针通常指向除了分配的内存之外的隐藏数据结构。然后free() 使用该隐藏的数据结构来正确释放指针指向的内存。因此,如果您将任何指针传递给free(),您也会得到未定义的行为,因为任何指针都没有关联的分配数据结构。
        • 是的,想想你将如何实现删除。你怎么可能知道指针指向的内存块有多大?你不能,不是没有一些上下文信息。此信息通常存储在指针之前的某个偏移量,并且删除将备份以读取该信息。您已删除将至少部分数据解释为其上下文信息,这意味着它无法正常工作(即未定义的行为)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-30
        • 2018-10-10
        • 1970-01-01
        • 2012-01-11
        • 1970-01-01
        相关资源
        最近更新 更多