21

我正在玩一些内存动态分配,但我不明白。当使用new语句分配一些内存时,我应该能够破坏指针指向的内存 using 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;  
}  

有什么线索吗?

4

9 回答 9

60

是时候了解什么是未定义的行为了。:)

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

在您执行 last*(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++ 并不关心。

于 2010-07-19T11:02:20.537 回答
11

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

建议您在调用 delete 后将指针设置为 0:

delete pTest;
pTest = 0;
于 2010-07-19T11:01:22.267 回答
5

delete 操作符调用对象的析构函数并释放之前分配给对象的内存。它不会影响指向已删除对象的指针变量。

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

于 2010-07-19T10:57:53.113 回答
5

答案是性能

使用无效值( , 等)填充所有释放的内存以捕获使用指向已释放内存的陈旧指针的尝试是一个很好的调试辅助0xCCCCCCCC工具0xDEADDEAD

But modifying a freed memory costs CPU time, so for performance reasons, the OS will just add the freed memory block to its "free" list, and leave the contents intact.

于 2010-07-19T11:27:02.373 回答
3

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

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

请注意,没有什么禁止new/delete分配器作为操作系统虚拟内存函数的瘦包装器工作,因此当与页面相关的所有已分配块都已被释放时,整个页面将被释放,并且任何访问它的尝试都会导致地址冲突.

TL,DR 版本:不要引用指向已释放内存的指针:它有时可能会起作用,有时会给你返回垃圾,有时会触发访问冲突。

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

于 2010-07-19T11:07:50.790 回答
1

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

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

于 2010-07-19T11:04:38.487 回答
1

破坏数据意味着什么?我想它可以把它归零,但为什么要麻烦呢?当我们从环境中得到它时,它被认为是脏的,那么为什么在我们返回它之前清洁它呢?我们不在乎里面有什么,因为我们放弃了阅读它的权利。至于为什么 delete 不会将指针本身归零:

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

于 2010-07-19T11:05:55.990 回答
1

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

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

delete pTest;

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

cout << *pTest << endl;

pTest 的打印值很可能是 3,就像您的情况一样。但是,打印的值也可能是 42。由于 pTest 指针被删除,它的内存被释放。因此,foo 指针可能会指向 pTest 在被删除之前所指向的内存位置。

于 2010-07-19T11:25:21.463 回答
0

Calling delete for a pointer marks the memory block pointed by it to 'free memory'. It doesn't destroy the items in the memory right away. The 'items' get destroyed only after the process has returned given that 'delete' was used for the pointer pointing to that memory block. In case 'delete' isn't used, it stays there forever resulting in a memory leak.

To solve your problem, just:

delete pTest;
pTest=NULL;
于 2021-04-20T12:06:40.317 回答