2010-07-19 167 views
20

我玩的内存动态分配一点,但我没有得到一个观点。当用new语句分配一些内存时,我应该能够销毁指针指向使用delete的内存。为什么不删除任何东西?

但是,当我尝试,这delete命令似乎没有工作,因为指针指向的空间似乎并没有被清空。

让我们的代码,这个真正的基本片为例:

#include <iostream> 

using namespace std; 

int main() 
{ 
    //I create a pointer-to-integer pTest, make it point to some new space, 
    // and fulfill this free space with a number; 
    int* pTest; 
    pTest = new int; 
    *(pTest) = 3; 
    cout << *(pTest) << endl; 

    // things are working well so far. Let's destroy this 
    // dynamically allocated space! 
    delete pTest; 

    //OK, now I guess the data pTest pointed to has been destroyed 
    cout << *(pTest) << endl; // Oh... Well, I was mistaking. 

    return 0; 
} 

任何线索?

+13

是的,一个小菜一碟,但不是一个小事。 – sharptooth 2010-07-19 11:24:38

回答

54

它的时间来了解什么是不确定的行为。 :)

在C++中,当你做一些非法/无意义/坏/等。该标准经常说“导致未定义的行为”。这意味着从那时起,您的程序状态将完全不受保证,任何事情都可能发生。

在你做最后的*(pTest)时,你会得到未定义的行为。这是因为pTest没有指向有效的对象,并且取消引用这样的指针是未定义的。所以你看到的是完全允许的:未定义的输出。

你所做的只是说“我完成了这个分配。”一旦你这样说,你就不应该(也确实不能)检查或关心那段记忆。它甚至没有概念意义去释放某些东西然后尝试使用它;你说过你已经完成了!

虽然您的输出有点可预测:您的操作系统可能会简单地说“好吧,感谢您的内存”,就是这样。它没有理由实际“重置”内存,或做任何特殊的事情。如果没有人(包括你自己的程序)没有使用它,那确实是浪费时间。

但请记住,此输出是完全未定义的。不要尝试使用不存在的对象。也许是一个更好的测试将是:

#include <iostream> 

struct foo 
{ 
    ~foo() 
    { 
     std::cout << "foo is gone :(" << std::endl; 
    } 
}; 

int main(void) 
{ 
    foo* f = new foo(); 
    delete f; // you'll see that the object is destroyed. 
} 

虽然看起来你希望看到内存本身会发生什么。请记住,摆脱记忆力然后尝试使用它是没有意义的,所以答案是:谁知道。这取决于您的特定平台,而C++并不关心这个平台。

+27

关于未定义行为的丑陋之处在于它大部分时间看起来不错。 – peterchen 2010-07-19 11:04:58

4

删除运算符调用对象的析构函数并释放先前分配给该对象的内存。 它不影响指向已删除对象的指针变量。

因此,当取消引用指向被销毁对象的指针时,您会遇到麻烦。

10

调用删除会将内存区域标记为空闲。它不需要重置其旧值。

建议您将鼠标指针设置为0,要求删除后:

delete pTest; 
pTest = 0; 
+2

我会建议在调用delete后不要使用变量_at all_(当然,除非它被重新分配给别的东西)。 – stakx 2010-07-19 11:28:19

+9

更好的解决方案是确保变量在删除后不久即超出范围,因此无法重新使用。 – 2010-07-19 12:22:30

0

它可能引用了任何映射内存。或者可能是未映射的内存,这取决于程序执行的时间长度,内存分配的细节,以及稍后库将内存返回到操作系统......

如果delete实际上清除了所有正在删除的内存,程序将花费更长的时间运行,因为它们会浪费大量的时间擦除内存,无论如何,这些擦除内存可能迟早会被覆盖。它可能对调试很有用,但在生产使用中,实际上并不需要调用内存内容。 (当然,加密密钥是个很好的例外;在调用deletefree之前对其进行清理是个好主意。)

1

什么会破坏数据呢?我想它可以将其归零,但为什么要麻烦呢?当我们从环境中获得它时,它会被认为是脏的,那么为什么在我们返回之前清理它呢?我们不在乎它的内容,因为我们放弃了阅读它的权利。而至于为什么删除不归零指针本身:

http://www2.research.att.com/~bs/bs_faq2.html#delete-zero

2

解引用指向释放的内存指针是未定义的行为。

很多时候它只会工作,因为new提供的内存通常是分配器管理的更大的分配内存块的一部分。当你调用delete时,它会调用相关的析构函数并将内存标记为空闲,这通常意味着“可以重新使用”。因此,查看该内存后,您会发现在调用delete之前存在的相同数据,或者在调用new之后重新分配了该内存块时的其他数据。

请注意,没有什么东西禁止分配器作为操作系统虚拟内存函数的薄包装器工作,所以当所有分配的块相对于页面被释放时,整个页面被释放,并且任何尝试访问它会导致地址违规。 TL,DR版本:不要指向指向释放内存的指针:它有时可能有效,有时会让你回到垃圾,有时它会触发访问冲突。

如果你犯这样的错误,立即注意的一个好方法是在删除指向它的内存后将指针设置为NULL:如果你的代码试图取消引用NULL指针,几乎在任何系统上使应用程序崩溃,所以像这样的错误不会被忽视。

1

只是一个简单的例子来说明可能发生什么,以及某些人提到的未定义的行为意味着什么。

如果我们的代码添加两个额外的行打印前:

delete pTest; 

int *foo = new int; 
*foo = 42; 

cout << *pTest << endl; 

PTEST的印刷值很可能是3,因为它是在你的情况。 但是,打印的值也可能是42.随着pTest指针被删除,其内存被释放。因此,有可能foo指针将 指向内存中的相同位置,pTest用于在删除 之前指向它。

5

答案是性能

这是一个很好的调试工具,用于填充所有释放的内存,并使用无效值(0xCCCCCCCC,0xDEADDEAD等)来捕获尝试将旧指针用于已释放的内存。

但修改释放的内存会花费CPU时间,因此出于性能原因,操作系统只会将释放的内存块添加到其“空闲”列表中,并保持内容不变。

相关问题