我最近发现代码中的很多地方都是这样的:
int * int_array = new int[1000];
// ... do things with int_array
delete int_array;
问题当然是它应该使用delete []
运算符,而不是常规delete
运算符。
谜团是:这个代码已经工作了几年,在Windows上由Visual Studio 2003和2005编译,在OS X上编译GCC / clang。为什么这不会导致事情在此之前发生严重错误?< / p>
据我了解,我们告诉编译器以“错误”的方式释放内存,通常如果你这样做,会发生一些可怕的事情并导致程序崩溃。为什么不发生这种情况?现代编译器会自动为您做“正确的事情”,还是对基本类型或其他内容无关紧要?我不能接受我们只是幸运,因为这些代码已被多个不同操作系统下的数千名客户使用多年。
请注意,我并不是在寻找一个做错事的借口,只是想了解为什么我们没有因为做错事而陷入困境。 :)
答案 0 :(得分:5)
这是未定义行为的本质 - 它可能完全符合您的预期。问题是,对于下一版本的编译器,操作系统,库或CPU ......它可能会做一些完全不同的事情。
最有可能的是,你有两个原因:
int
没有析构函数。因此,无法正确销毁阵列中的每个元素都没有后果。
在此平台上,new
和new[]
使用相同的分配器。因此,您不会将块返回给错误的分配器。
答案 1 :(得分:3)
让我们一步一步地完成所发生的事情(但我们会忽略异常):
int *foo = new int[100];
这会分配100*sizeof(int)
字节的内存并调用int::int()
100次,对于数组中的每个元素都会调用一次。
由于int
是内置类型,它有一个简单的构造函数(即,它什么都不做)。
现在,怎么样:
delete foo;
这将在int::~int()
指向的地址上调用foo
,然后删除foo
指向的内存。同样,由于int
是一个内置类型,它有一个简单的析构函数。
将其与:
进行比较delete [] foo;
将为int::~int()
指向的数组中的每个项调用foo
,然后删除foo指向的内存。
这里的基本区别是元素1..99不会被破坏。对于int
这不是问题,这可能是您现有代码有效的原因。对于具有真正析构函数的对象数组,您将看到更多的不当行为。
P.S。大多数实现都会删除foo
所指向的整个内存块,如果你编写delete foo;
即使你用array-new分配它也是如此 - 但是如果依靠它那么你将是愚蠢的。
答案 2 :(得分:1)
这不起作用。删除没有[]
的数组会泄漏内存。如果您的应用程序运行很长时间,它将影响性能。对于短期计划,没有问题。
需要注意的另一件事是delete
会破坏new
分配的所有内存。如果您已按new
(而非new []
)分配了数组,则可以使用delete
来销毁它。
答案 3 :(得分:1)
这不会导致程序崩溃,但每次执行此操作时都会泄漏999 * sizeof(int)
个字节的内存。
答案 4 :(得分:1)
常规delete
运算符实际上可以释放数组中的所有内存,具体取决于分配器,但真正的问题是它不会在数组元素上运行任何析构函数。