为什么这个程序不消耗内存?

时间:2011-05-21 20:03:41

标签: c++ arrays delete-operator

令我困扰的是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在哪里存储它的数据?

(顺便说一下,所有程序都是使用no-optimization编译的!)

现在主要问题是:

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

5 个答案:

答案 0 :(得分:5)

  

令我困扰的是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)到a   指针类型。结果有类型   空隙。

     

2 如果操作数有类类型,则   操作数转换为指针类型   通过调用上述内容   转换功能,并转换   操作数用于代替   原始操作数为剩余的   这个部分。无论哪种方式,   如果删除操作数的值   是操作具有的空指针   没有效果。在第一个替代方案中   (删除对象),值的   delete的操作数应为指针   到非数组对象或指向   表示基础的子对象(1.8)   这类物体的一类(第10条)。   如果不是,则行为未定义。   在第二个备选(删除数组)中,操作数的值   delete应为指针值   这是由前一个数组产生的   新的表达。如果没有,那就是   行为未定义。 [注意:这个   意味着的语法   delete-expression必须与类型匹配   由new分配的对象,而不是   new-expression的语法。 ]

“未定义的行为”意味着可能发生任何事情,包括您刚刚描述的行为。

您在测试#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);之类的东西。

答案 1 :(得分:2)

当你说“不消耗任何内存”时,你是在谈论在“任务管理器”风格的性能监视器中查看?因为如果是这样的话,40字节不会显示为“内存使用”......你将不得不为大多数标准流程性能监视器分配更多的内存。

答案 2 :(得分:1)

delete [](d + 5);

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

您的测试结果将取决于许多事情,其中​​包括您运行这些测试的平台,new/delete的内部等。

答案 3 :(得分:1)

delete [](d + 5);

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

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

在某些内存管理器上,如果你要求它们通过传递一个指针释放一个块,如果你没有将指针传递给该块的开头,那么它们可以释放包含的整个块你传递的指针。在您的情况下可能会发生这种情况。

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

答案 4 :(得分:0)

您是否在发布版本下运行这些测试?你看过生成的装配说明了吗?你如何衡量内存消耗?你正在分配40个字节然后立即清除它。

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