"delete pointer"仅仅意味着"*pointer = 0"吗?

5
# include <iostream>

int main()  
{  
using std::cout;
int *p= new int;

*p = 10;
cout<<*p<<"\t"<<p<<"\n";
delete p;
cout<<*p<<"\t"<<p<<"\n";

return 0;

}

输出:
10 0x237c010
0 0x237c010

在删除 p 之后,为什么指针 p 仍然保留其值?删除不会释放指针 p 吗?
“释放指针” 究竟是什么意思?
“delete p” 是否只意味着 “*p = 0”?(从输出中看似乎是这样的)


好吧,它至少也删除了指针所指向的数据,但我相信很快就会有更好的答案解释“删除”内存实际上意味着什么。我怀疑它甚至可能不会将内存清零,而只是允许重新分配该空间。 - prelic
2
运用一些批判性思维——对于例如std::string*p = 0会如何工作? - ildjarn
5个回答

6
在删除p之后,为什么指针p保留其值?
这是语言设计的特点。如果您希望保持所持有的指针归零,您需要自己将其赋值为零。指针p是另一个内存块,与其指向的分配/对象分开。
delete不会释放指针p吗?
它调用对象的析构函数并将内存返回给系统(类似于free)。如果是数组(delete []),则会调用所有元素的析构函数,然后返回内存。
“释放指针”的确切含义是什么?
当您需要从系统获得一块内存时,您需要分配它(例如使用new)。当您完成使用时,您使用相应的free/delete调用将其返回。它是一种资源,必须归还。如果没有归还,则您的程序将泄漏(这是没有人想要的)。

@Justin,请问您能解释一下为什么在上述代码中删除后指针p仍然保留其值吗? - Avinash Sonawane
3
为什么释放内存会改变其内容? - ildjarn
@AvinashSonawane - 请注意,在删除数据后访问它是不安全的,即使在删除之后它可能仍然保持其原始值。 - prelic
2
@AvinashSonawane:有关详细信息,请参见我在该网站上的最佳答案。访问已释放的内存与访问超出范围的变量指针没有任何区别。 - Nicol Bolas

2
为了理解释放内存的含义,首先必须了解分配内存的含义。以下是一个简化的解释。
存在着内存。内存是一大块可以访问的东西。但是因为它是全局的,所以需要一些方法来划分它。需要一些方法来管理谁可以访问哪些内存片段。一个管理内存分配的系统称为“堆”。
堆占有一些内存(一些由栈和静态数据拥有,但现在不要管这些)。在程序开始时,堆会说你没有访问任何堆内存的权限。 new int做的两件事情。首先,它向堆系统请求:“我想要一个适合存储int类型值的内存块。”它得到了指向这个内存块的指针:一个堆内存块,你可以在其中安全地存储和检索一个int类型的值。
您现在拥有一个int大小的内存。堆保证只要遵循其规则,您放置在那里的任何内容都将被保存,直到您明确更改为止。这是您与万能堆之间的契约。 new int的另一件事是用一个int值初始化该堆内存块。在这种情况下,它是默认初始化的,因为没有传递值(new int(5)将使用值5进行初始化)。
从此时起,您可以在这个内存块中合法地存储一个int。您可以检索存储在那里的int。您还可以做一件事:告诉堆您已经完成使用该内存。
当您调用delete p时,会发生两件事。首先,p被去初始化。再次强调,因为它是一个int,所以什么也不会发生。如果这是一个类,则将调用其析构函数。
但在此之后,delete会向堆发送消息:“嘿,堆:你给我的指向int的指针还记得吗?我现在用完了。”堆系统可以做任何它想做的事情。一些堆在调试版本中会清除内存。但在发布版本中,内存可能不会被清除。
当然,堆可以随心所欲地做任何事情的原因是,一旦您删除该指针,就会与堆进入新的协议。以前,您请求一个适合存储int类型值的内存块,堆满足了您的要求。您拥有了该内存,堆保证它是您的,只要您想要它。您放置在那里的内容将保留在那里。

在你玩耍后,你将其放回了堆中。这就是合同的作用。当你说delete p时,针对于任何对象p,你所说的是:

我郑重承诺不再触碰此内存地址!

现在,如果你再次调用new int,堆可能会把那个内存地址还给你,也可能会给你另一个内存地址。但你只能在newdelete之间的时间访问由堆分配的内存。

鉴于此,这是什么意思?

delete p;
cout << *p << "\t" << p << "\n";

在C++中,这被称为“未定义行为”。C++规范有很多被声明为“未定义”的事情。当你触发未定义行为时,任何事情都可能发生!*p可能为0。*p可能是它曾经的值。*p的操作可能会导致程序崩溃。
C++规范是您和编译器/计算机之间的合同。它说明了您可以做什么,以及系统如何响应。"未定义行为"是当您违反合同,即执行C++规范所说不应该执行的操作时发生的情况。此时,任何事情都可能发生。
当您调用delete p时,您告诉系统您已经完成了对p的使用。通过再次使用它,您向系统撒谎。因此,系统不再必须遵守任何规则,例如存储您想要存储的值。或继续运行。或不从您的鼻子中产生恶魔。或其他。
您违反了规则。您必须承担后果。
因此,delete p不等同于*p = 0。后者仅意味着“将0设置到p指向的内存中”。前者意味着“我已经完成了对由p指向的内存的使用,在您告诉我可以使用之前,我不会再次使用它。”

1
删除p后,为什么指针p保留其值?delete不会释放指针p吗?
它释放指针所指向的内存(在调用任何适当的析构函数之后)。指针本身的值不变。
“释放指针”到底是什么意思?
如上所述-它意味着释放指针所指向的内存。
delete p只是意味着*p = 0吗?(从输出中似乎如此)
不是。系统不必向被释放的内存写入任何内容,如果确实写入了某些内容,也不必写入0。但是,系统通常必须以某种方式管理该内存,并且可能实际上会写入指针指向的内存区域。此外,刚释放的内存块可以分配给其他东西(在多线程应用程序中,这可能会在delete操作甚至返回之前发生)。该内存块的新所有者当然可以向该内存写入任何他们想要的内容。
一个指向已释放内存块的指针通常被称为“悬挂”指针。对于悬挂指针(无论是读还是写)进行解引用是一种错误。有时你会看到代码在删除指针之后立即将NULL0赋给指针,有时使用一个宏或函数模板同时删除和清除指针。请注意,这并不能修复所有关于悬挂指针的错误,因为其他指针可能已经被设置为指向该内存块。
现代处理这些问题的方法是避免使用裸指针,而是使用智能指针,如shared_ptrunique_ptr

0

delete p 简单地释放了调用 new 运算符分配的内存。它不会改变指针的值或已释放内存的内容。


delete 确保调用析构函数,这可能会改变指针的值或释放内存中的内容。 - Cactus Golov
@Narrakan- 如果指针值和指向的值都没有改变,那么释放内存到底有什么作用呢?由于指针值没有改变,任何人都可以访问该“已删除”对象。 - Avinash Sonawane
@AvinashSonawane 嗯,正如你自己所看到的那样,即使在你删除了它之后,你仍然可以访问 *p,因此你仍然可以访问那个内存,尽管这是未定义的行为。 - Pietro Lorefice
@Avinash 释放的内存会返回到堆中,然后可以用于分配其他对象。如果您不以这种方式回收已释放的内存,则应用程序的内存消耗将持续增长,直到崩溃。如果该应用程序是控制飞机发动机的嵌入式设备,那么飞机也将崩溃。 - Crashworks

0
(请注意以下内容并非实际工作原理,仅供参考。)
在 new 的实现中,它会保留一个可用内存列表。当你使用“int *p = new int;”时,它会从可用内存列表中切割出一个 int 大小的块,并将其分配给你。当你运行“delete p;”时,它会被放回可用内存列表中。如果你的程序连续调用了 30 次 new 而没有调用 delete,你将从 new 中获得 30 个不同大小的 int 块。如果你连续调用了 30 次 new 然后再调用 delete,你可能(但不一定)会获得相同大小的 int 块。这是因为当你调用 delete 并表示不再使用该内存时,new 可以自由地重复使用它。
简而言之,delete 通知 new 这段内存再次可用,它不会影响你的变量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接