1

我最近发现我们代码中的很多地方都在做这样的事情:

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 编译时,已经运行了好几年。为什么这之前没有导致事情发生严重错误?

据我了解,我们告诉编译器以“错误”的方式释放内存,通常如果你这样做,会发生可怕的事情并且你的程序崩溃。为什么这不会发生在我们身上?现代编译器是否会自动为您“做正确的事情”,或者是否足够正确的事情对基本类型无关紧要,或者其他什么?我不能接受我们只是运气好,因为这段代码已经使用了多年,在多种不同的操作系统下被成千上万的客户使用。

请注意,我不是在为做错事找借口,只是想了解为什么我们不会因为做错事而陷入困境。:)

4

5 回答 5

5

这是未定义行为的本质——它可能完全按照您的意图去做。问题是,对于编译器、操作系统、库或 CPU 的下一版本……它可能会做一些完全不同的事情。

最有可能的是,您逃脱它有两个原因:

  1. int没有析构函数。因此,未能正确销毁数组中的每个元素没有任何后果。

  2. 在这个平台上,newnew[]使用相同的分配器。因此,您不会将块返回给错误的分​​配器。

于 2013-02-03T20:04:07.390 回答
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这不是问题,这可能就是您现有代码有效的原因。对于具有真正析构函数的对象数组,您会看到更多的不当行为。

PS即使您使用array-new分配它,大多数实现都会删除指向的整个内存块,但foo如果delete foo;您指望它,您将是愚蠢的。

于 2013-02-04T04:50:49.867 回答
1

这不起作用。不删除数组[]会泄漏内存。如果您的应用程序长时间运行,它会影响性能。对于短命的节目不会有问题。

另一件需要注意的事情是delete会破坏所有被分配的内存newnew如果您已通过(not )分配了一个数组,new []则可以使用它delete来销毁它。

于 2013-02-03T20:00:15.467 回答
1

这不会导致程序崩溃,但999 * sizeof(int)每次这样做都会泄漏内存字节。

于 2013-02-03T20:00:27.483 回答
1

常规delete运算符实际上可能会从数组中释放所有内存,具体取决于分配器,但真正的问题是它永远不会在数组元素上运行任何析构函数。

于 2013-02-03T20:01:13.547 回答