2010-05-24 69 views
9

我有一个非常简单的C++代码在这里:(访问冲突)解除引用删除的指针总是会导致访问冲突?

char *s = new char[100]; 
strcpy(s, "HELLO"); 
delete [] s; 
int n = strlen(s); 

如果我按F5(启动调试,)这总是导致崩溃运行从Visual C++ 2008这个代码但是,在开始这个可执行文件在IDE之外,或者使用IDE的Ctrl + F5(无调试开始)不会导致任何崩溃。有什么区别?

我也想知道是否有可能稳定地重现访问已删除区域导致的访问冲突崩溃?在现实生活中这种崩溃是罕见的吗?

+3

它产生未定义的行为,这意味着该程序可以做任何想做的事情。不过,它有点奇怪,它不会在调试器外面崩溃。究竟发生了什么?如果这是一个控制台程序,输出是什么? – 2010-05-24 10:36:01

+3

照顾已删除的指针确实很痛苦。这就是为什么你应该使用'std :: vector'或者在这个特殊情况下'std :: string'而不是'new []'。你不应该使用'delete'。 – avakar 2010-05-24 10:55:28

+0

代码示例是从实际应用程序中删减的。我正在重现一个很少发生的bug,可能只有当一个线程执行'delete'并且另一个执行取消引用时。所以我想要一种稳定重现这一点的方法。所以所有这些有用的答案都没有让我失望:) – Gant 2010-05-24 16:32:26

回答

16

通过删除的指针访问内存是未定义的行为。你不能指望任何可靠/可重复的行为。

在一种情况下,它最有可能“起作用”,因为字符串仍然“坐在那里”在现有的内存中 - =但你不能依赖它。 VS使用调试值填充内存,以帮助强制崩溃以帮助查找这些错误。

5

delete后取消引用指针是不确定的行为 - 任何事情都有可能发生,包括但不限于:

  • 数据损坏
  • 访问冲突
  • 没有明显的影响

确切结果将取决于多数因素无法控制的多种因素。你首先不会触发未定义的行为会好得多。

2

通常,从流程的角度来看,分配和释放内存没有区别。例如,这个过程只有一个大的内存映射可以根据需要增长。

访问冲突是由于读取/写入不可用的内存引起的,通常不会被分页到进程中。各种运行时内存调试实用程序使用分页机制来跟踪无效的内存访问,而没有软件内存检查所具有的严重运行时间损失。

反正你的例子只能证明,在一个环境中运行该程序时,有时会检测到错误,但在另一个环境中没有检测到,但它仍然是一个错误,上面的代码是不确定的行为。

8

不同之处在于调试器,调试库和内置“调试”模式的代码喜欢破坏应该中断的东西。你的代码应该中断(因为它访问的内存不再是技术上拥有的),所以在编译调试和在调试器中运行时,它会更容易中断。

在现实生活中,你通常不会得到这样的不太通知。所有那些让它们在调试器中崩溃的东西......这些东西很贵。所以在发布时不会严格检查。您可能能够在100次中使用99次,以释放一些内存并在之后立即访问它,因为运行时库不会将内存立即交回操作系统。但是,第100次,无论是内存消失了,还是另一个线程现在拥有它,并且你得到的不再是一个字符串的字符串的长度,而是一个252462649字节的垃圾数组,只要你或运行时应该关心)存储器。而且几乎没有什么可以告诉你发生了什么。

所以不要这样做。一旦你删除了一些东西,就认为它已经消失了。或者你会浪费一半的时间跟踪海森堡。

+0

很好的参考heisenbugs:D – 2010-05-24 12:00:47

1

我也想知道是否有可能 稳定地再现访问 导致的访问 冲突崩溃删除区域?

而不是普通的delete您可以考虑使用内联函数,该函数还将删除的指针的值设置为0/NULL。如果你引用它,这通常会崩溃。但是,如果您再次删除它,它不会投诉。

难道这种碰撞难得在 真实?

不,这种崩溃可能是您和我在软件中看到的大多数崩溃的幕后。