[严正声明]关于delete built-in型数组可以不带[]的谬论!

visame 2008-02-21 08:41:37

/*实在是看不下去了。这几天到处有人说这个。于是转篇文章给大家看看。
有哪一本书说过可以不带[]吗?(C++ Primer/The C++ Programming Language/C++ Standard)
请找出来看看!
[16.13] Can I drop the [] when deleteing array of some built-in type (char, int, etc)?

No!
Sometimes programmers think that the [] in the delete[] p only exists so the compiler will call the appropriate destructors for all elements in the array. Because of this reasoning, they assume that an array of some built-in type such as char or int can be deleted without the []. E.g., they assume the following is valid code:


void userCode(int n)
{
char* p = new char[n];
...
delete p; // ← ERROR! Should be delete[] p !
}
But the above code is wrong, and it can cause a disaster at runtime. In particular, the code that's called for delete p is operator delete(void*), but the code that's called for delete[] p is operator delete[](void*). The default behavior for the latter is to call the former, but users are allowed to replace the latter with a different behavior (in which case they would normally also replace the corresponding new code in operator new[](size_t)). If they replaced the delete[] code so it wasn't compatible with the delete code, and you called the wrong one (i.e., if you said delete p rather than delete[] p), you could end up with a disaster at runtime.

...全文
1161 34 打赏 收藏 转发到动态 举报
写回复
用AI写文章
34 条回复
切换为时间正序
请发表友善的回复…
发表回复
flyingscv 2008-02-23
  • 打赏
  • 举报
回复
楼主用的啥编译器报的错?

一般来说,释放类数组(如果不需要析构)也可以不用[].编译器会把new []和delete []优化为new和delete一样的操作
hxxwcc 2008-02-23
  • 打赏
  • 举报
回复
至少这样做会减少可移植性
yuanchuang 2008-02-22
  • 打赏
  • 举报
回复
内建类型没影响吧。
但是不用[]的人是标准没事找事干。
hxxwcc 2008-02-22
  • 打赏
  • 举报
回复
支持用new & delete后面的[]应成对出现,可读性好,复合标准

holyfire 2008-02-22
  • 打赏
  • 举报
回复
当然要用[],因为全局new delete也有可能被修改呀,为了保证一定能有正确行为,一定要正确的进行匹配。
Oversense 2008-02-22
  • 打赏
  • 举报
回复
还是用 [] 别偷懒
晨星 2008-02-22
  • 打赏
  • 举报
回复
更正前面的一个严重错误“new申请的用new释放”应该是“new申请的用delete释放”。^_^
hydvivian 2008-02-22
  • 打赏
  • 举报
回复
还是ls说得好。
晨星 2008-02-22
  • 打赏
  • 举报
回复
要么就像楼主那样深入地去学习和钻研,不断深入地多问几个为什么;
要么就懒一点,但同时老老实实的,new申请的用new释放,new[]申请的用delete[]释放,别问为什么。
白乔 2008-02-22
  • 打赏
  • 举报
回复
这种无聊的问题还在讨论,看来不知道标准C++还是很多的啊~

antares_sco 2008-02-22
  • 打赏
  • 举报
回复
顶5楼
michney 2008-02-22
  • 打赏
  • 举报
回复
好习惯要保持
不好习惯坚决克服
deng2000 2008-02-22
  • 打赏
  • 举报
回复
也来凑个热闹. 我百分百赞成需用delete[]来释放数组. 想补充一点的是, 对于class的情况, 用delete释放数组有可能造成远比漏调析构函数(内存泄漏)更严重的问题!
请看如下这个最简单的程序:

#include <stdio.h>

class A
{
public:
~A() {};
int n;
};

int main()
{

A* arr = new A[8];

printf("before delete\n");
delete arr;
printf("after delete\n");

return 0;
}

在VC(6.0/VS2003/VS2005)中用debug方式编译运行, 其结果是, 输出"before delete"后程序就出保护错退出了. "after delete"根本没有显示出来!

其原因在于, 在VC中,对于有显式析构函数的对象, 在分配数组时其前会有一个4字节前缀用来保存数组元素个数. 如果用delete来释放数组, 就会导致释放的内存地址与分配时的内存地址出现4字节偏差, 而导致灾难性的保护错.

这个例子提醒我们, 还是遵照C++标准比较稳妥, 呵呵.

xuegao007 2008-02-22
  • 打赏
  • 举报
回复
我学c++也对这事犯嘀咕呢。呵呵
fish6344 2008-02-22
  • 打赏
  • 举报
回复
曾经,在论坛上讨论过这个问题,我根据很多经典文献上大师们的阐述,极力主张,对于堆上的数组,一定要以delete[]处理之。当时有位朋友指出,说现代的编译器对释放数组已有优化,可以直接以delete而不是delete[]删除。并问我有没有研究过今天的编译器,是死搬经典...

接受这位仁兄的建议,我以如下代码在VC7.1上作了肤浅的测试:

int main(void)
{
while(1)
{
int *_p = new int[];
delete _p;//!
}
}

的确,从任务管理器中没有看到内存泄漏。

但,我仍然非常同意Vitin朋友的意见,那就是"对于所有的undefined behavior,我们能做的就是避免它们"!

而且,我非常反对上述优化(如果有的话)!因为,它既不符合C++标准,且改变和模糊了正确的C++堆内存的释放语意:

我们能从代码: delete _p;看出什么呢?



Vitin 2008-02-22
  • 打赏
  • 举报
回复
支持楼主。

毫无疑问,用delete释放new[]分配的内存是一种undefined behavior.

知道什么是undefined behavior吗?
首先,C++标准不定义它。因此,这样的代码不具备可移值性,在不同编译器上可以得到不同的结果。
其次,C++标准不保证其正确。因此,这样的代码不总是正确的,在某个编译器上正确不意味着在其他编译器上正确。这里“正确”是指程序正常运行,不出现运行时错误。
第三,甚至编译器也不提供类似的保证。即在某个编译器上的当前版本上正确运行,并不意味着在未来版本上也可以正确运行;即使正确,也不一定会得到同样的结果。
最后,即使在某个上下文中正确运行,也不意味着在其他上下文中也可以正确运行(或得到相同结果),即使它们使用了同一版本的某编译器。因为,这段代码的使用范围是不确定的(没有人告诉你范围是什么,需要你自己去确定),它可能在一些情形下恰好正确,但在其他情形中完全崩溃。

对于这样的代码——如果你仍然想采用它们——首先必须研究你当前使用的编译器,确定它的表现符合你的要求(这需要一些“黑客”精神)。假设这一点已经满足,那么你可以有限地使用它们了。但是请注意了:你不能换编译器,除非你对新编译器重新开始研究;你不能升级编译器,因为那样的话你仍然需要重新开始研究;你甚至不能将它们随处使用,因为它们可能无法适应新的用途(比如当用户自定义operator delete或operator delete[]的情况);当然也不必说将代码交给别人使用,除非他和你一样愿意为此付出上述所有代价。

很多时候,经验会决定人的行为。比如我们都能够轻易避免如下的错误:

int arr[10];
arr[10] = 100;

因为在刚刚接触数组时,我们往往已经承受过数组越界访问的后果(不管是错误的运行结果还是导师的批评)。但是对于new[]和delete的情况,获得“积极”经验的机会就小很多(甚至,会在某些编译器上获得“消极”经验。相信我,这不是编译器设计者的错——他们本就不该承担导师的责任,又或者,他们希望其产品能够容忍使用者的某些错误用法)。但是,经验不能决定代码的行为。我们讨论的问题和上述数组越界访问一样,都是undefined。而当一段代码的行为是undefined时,它就迟早会令你目瞪口呆,如果不是今天,就会是明天、明年、或十年以后……

最后需要澄清的是,或许有人会对内建类型和自定义类型给出不同的建议(除却用途限制如自定义operator delete),理由是内建类型不必调用析构函数。这种说法并非完全没有道理。析构函数当然是一个必要的因素;但,也仅此而已。注意了,它是必要的,而不是充分的!当有人提出上述建议时,他已经是在以“黑客”的立场来看问题了:他在揣摩编译器的实现机制,其依据则是C++标准所列出的需求。再次声明,这些需求只是必要条件!任何编译器都可以在这些需求之外加入新的东西(前提是不与标准相冲突)。因此除了调用析构函数,在delete语句中完全可以做其他的事,有些事是仅适用于自定义类型的,有些仅适用于内建类型,有些则适用于所有类型!用一个因素代替所有因素,(哪怕它再重要也)不会导致正确的结论,甚至不是一个“黑客”所需要的品质。也不要用诸如“效率”的原则去估计编译器设计者的行为:你不是他们,你怎么知道他们所做的事不是必须的呢?

所以,结论很简单:对于所有的undefined behavior,我们能做的就是避免它们。
MagiSu 2008-02-22
  • 打赏
  • 举报
回复
Let's talk based on compiled ASM:

Debian Lenny/Sid, g++ 4.2.2

int* p=new int[10];
0x80484e5 movl $0x28,(%esp)
0x80484ec call 0x8048420 <_Znaj@plt>
0x80484f1 mov %eax,-0x8(%ebp)
delete p;
0x80484f4 mov -0x8(%ebp),%eax
0x80484f7 mov %eax,(%esp)
0x80484fa call 0x8048400 <_ZdlPv@plt>

p=new int[10];
0x80484ff movl $0x28,(%esp)
0x8048506 call 0x8048420 <_Znaj@plt>
0x804850b mov %eax,-0x8(%ebp)
delete [] p;
0x804850e cmpl $0x0,-0x8(%ebp) ; NOTE
0x8048512 je 0x804851f <main+75> ; A CHECK HERE
0x8048514 mov -0x8(%ebp),%eax
0x8048517 mov %eax,(%esp)
0x804851a call 0x8048430 <_ZdaPv@plt>
0x804851f mov $0x0,%eax

The only difference between "delete p" and "delete[] p" is the verification of the size of the object being deleted.
czp_opensource 2008-02-22
  • 打赏
  • 举报
回复
mark
ssdx 2008-02-22
  • 打赏
  • 举报
回复
delete不带[],对于内置类型数据没有问题,但是对于自定类,不能保证析构函数在数组每个对象上调用,当调用delete删除,只能在数组首元素调用析构函数,其它则未调用,内存泄漏。
sheenl 2008-02-22
  • 打赏
  • 举报
回复
标准当然应该用delete[]。 (实际上也应该用)

但是在我用过的编译器中, 我还没有遇到过用delete来释放buit-in型数组会出问题的。

就好比标准虽然有export模板的语法, 但是常见的编译器没几个支持的一样。
加载更多回复(14)

64,660

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧