C++删除动态数组只删除第一个元素

C++ Deleting dynamic array only deletes first element

本文关键字:删除 第一个 元素 数组 动态 C++      更新时间:2023-10-16

我最近开始使用指针和动态数组,并学习如何理解它们。我正在关注C++初级读本(第5版),我相信这本书是2005年的,所以我有点不知道发生了什么,因为我觉得有些东西已经更新了。

我在这里和其他地方搜索了一下,这是故意的行为,还是我只是做错了什么。

int main()
{
double * p3 = new double [3];
p3[0] = 0.2;
p3[1] = 0.5;
p3[2] = 0.8;
cout << "p3 = " << p3[0] << ", " << p3[1] << ", " << p3[2] << endl;
p3 = p3 + 1;
cout << "Incrementing p3 = " << p3[0] << ", " << p3[1] << ", " << p3[2] << endl;
p3 = p3 - 1;
cout << "Decrementing p3 = " << p3[0] << ", " << p3[1] << ", " << p3[2] << endl;
delete [] p3;
cout << "After deleting p3 = " << p3[0] << ", " << p3[1] << ", " << p3[2];
return 0;
}

正在发生的事情是,在书中,它说;只要delete中的括号为空,它就应该删除整个动态数组,而在我的情况下,它只删除第一个元素(p3[0])。

我只是想知道它是否真的释放了内存,但因为我继续使用指针,它会产生一些奇怪的行为,或者这是应该发生的事情,而代码是错误的。

编辑:程序的输出

p3 = 0.2, 0.5, 0.8
Incrementing p3 = 0.5, 0.8, 1.37017e-309
Decrementing p3 = 0.2, 0.5, 0.8
After deleting p3 = 1.63786e-305, 0.5, 0.8

您甚至在删除任何内容之前就有未定义的行为。在调整CCD_ 1之后,访问CCD_。在释放内存块之后取消引用指针也是UB。不,你们不能只释放分配区块的第一个项目,只能释放整个区块。请注意,您还需要使用与用于分配的方法完全相反的free方法:new-deletenew []-delete []或(C样式)malloc-free

您错过的是,像double这样的标准值类型没有析构函数。你看,根本不需要触摸他们的内存来"清除"它,只要知道内存可以重复用于其他事情就足够了。

因此,当您在数组上调用p3 = p3 + 1;0时,内存中的大部分数据都保持不变。但是,delete[]决定立即重用数组中第一个double占用的内存来存储其一些内部数据。这就是你在里面看到1.63786e-305的原因。这不是存储的双精度,很可能是指向其他内部数据的指针。

在调用delete[]之后触摸阵列的内存是未定义的行为。如果你这样做,任何事情都可能发生。所以,一定不要这样做。即使只是像你那样读取内存,也会立即破坏你的进程!

您可以断言您的delete[]确实删除了所有数组元素,方法是在实际具有析构函数的元素数组上调用它:

#include <iostream>
class Foo {
public:
Foo() { std::cout << "constructing Foo at " << (intptr_t)this << std::endl; }
~Foo() { std::cout << "destructing Foo at " << (intptr_t)this << std::endl; }
};
int main() {
Foo* array = new Foo[3];
delete[] array;
return 0;
}

在我的机器上运行这个小测试程序,我得到以下输出:

在93925438098472处构建Foo
在93925 438098473处构建Foo
9392543809 8474处破坏Foo
939254389 8473处破坏Foo

正如您所看到的,所有三个对象都被构造,然后所有三个物体都以相反的顺序被销毁。一切都很好。

执行语句delete [] p3;时,为数组分配的内存将被释放回操作系统,然后操作系统可以将该内存重新用于正在运行的任何进程。在PC上,内存通常仍会保留给同一个(您的)进程,因此访问它不会立即导致分段冲突(尽管这是未定义的行为)。然而,它可能被用于其他目的(例如,将其他信息存储在堆上的那个位置)。删除p3后,p3[0]引用的位置就是这样。

释放内存并不意味着它将被清除(所有字节重置为0)。这就是为什么在访问p3[1]和p3[2]时仍然可以看到旧值的原因。这只是意味着,内存不再受到任何保护,而不会被进程显式写入该地址。

您无法(据我所知)知道堆上的某个地址目前是否保留供您使用,这就是为什么建议在调用delete后立即将指针设置为NULL的原因。

delete [] p3; p3 = NULL; // or for C++11: p3 = nullptr;

通过这种方式,您可以自己标记:当指针为NULL时,我已经释放了可能与它相关联的内存。