#include <iostream>
#include <cstdio>
using namespace std;
int main(void)
{
int arr[] = {1,4,2,3,5,6};
int *p = arr;
delete p;
for(int i = 0 ; i < 6; i++)
cout << p[i];
return 0;
}
输出为142356
。
当我说delete p
为什么不删除p?
运行代码时不应该出现分段错误吗?
答案 0 :(得分:4)
正如一位聪明的中国老僧人曾经说过的那样,“未定义的行为是未定义的”。
说真的,尝试delete
不是new
d的东西是未定义的。
c和c ++都将其大部分意外功能定义为“未定义”。这意味着当你有这种行为时,你不知道会发生什么。如果你真的很幸运,它会出现故障或类似情况。更常见的是,会发生奇怪且无法解释的事情,试图推理它们在很大程度上是毫无意义的。
这样做的主要原因是编译器的实现既简单又更优。
作为一名C ++程序员,我建议你阅读Undefined Behavior,Unspecified Behavior和Implementation Defined Behavoir。它们对于理解任何适度有趣的c ++代码将如何运作都是至关重要的。
LLVM项目的blog关于未定义的行为是一个很好的阅读
答案 1 :(得分:2)
您有未定义的行为。
int arr[] = {1,4,2,3,5,6};
int *p = arr;
delete p;
此处,arr
创建时未动态分配。甚至你的指针p
也没有动态分配。您无法在其上使用delete
。当您使用delete
[或delete[]
]分配它,然后使用new
释放动态分配的内存时,将使用new[]
[或delete
]。
定义指针(或声明)不会导致分配内存。您使用new
int * p = new int; //later deleted as delete p;
或
int * p = new int[5]; //later deleted as delete[] p;
等
分配内存,然后删除它。
(此外,new
和delete
的数量必须相等并且在您的代码中对应)
答案 2 :(得分:1)
你正在使用“未定义的行为”,在这种情况下使用免费后的指针(并释放一些不是来自新的东西) - C ++(如C)设计得很快。因此,它不会执行“不必要的”操作,例如在删除对象时检查内存是什么或用其他内容填充内存。它只是将内存标记为“空闲”,并且当您稍后分配其他内容时,它将(或可能选择)将该内存用于新分配。
如果你有一个类而不是一个简单的int
,你可以让析构函数写入类的成员变量并清除它。但对于内置类型,例如int
,long
,double
等,则无法完成任何操作。
请注意,不明确的行为只是 - 未定义。它可以并且将会做除你期望之外的其他事情 - 但它也可以完美地完成你的期望。部分原因取决于您的期望。关键是标准没有说明当你做某事时将会发生什么(例如,标准没有声明“使用已经使用删除释放的内存必须导致崩溃”或“必须将通过删除释放的内存设置为值X“ - 它也没有说使用释放的内存不会导致崩溃,或者内存无法更改为某个新值。
同样地,如果您释放未分配新内容的内容,标准也没有说明您应该期待什么。相反,它说它是“未定义的行为”。最有可能的是,如果您现在尝试使用new
,它可能会失败,因为您的堆已经搞砸了。
答案 3 :(得分:0)
delete
运算符用于通知管理内存的任何人,程序不再需要已分配的内存,哪个地址存储在已删除的指针中,并且可以被释放。
请记住,C ++不仅针对Win32或Linux进行了编译。可能有更多的平台,以不同的方式管理他们的记忆。这就是为什么标准没有明确定义,释放内存会发生什么。在您的特定情况下,内存被标记为空闲(如果您分配并填充了该大小的另一个数组,之前的,已释放的一个可能已被覆盖,但不一定),但其内容未被破坏。
内存管理的这种实现有助于提高性能:用零(或其他值)覆盖释放内存的重点是什么,如果很快就会再次使用并填充其他数据?
现在为什么你试图从那个内存中读取没有引起AV?可能,因为整个内存块(该阵列所在的位置)是为您的进程保留的,所以它仍然试图从自己的内存中读取。我猜测,尝试写入该内存可能导致AV(但可能没有,它也是特定于实现的。)
一般规则就是不使用由删除释放的内存,因为 - 正如标准所说 - 你不能保证它的可访问性,也不保证它被释放后的内容。