执行free()操作符从动态变量中删除地址

Does free() operator delete the address from the dynamic variable?

本文关键字:变量 删除 地址 动态 free 操作符 执行      更新时间:2023-10-16

让我们考虑下面的程序:

int main ()
{
    int *p, *r;
    p = (int*)malloc(sizeof(int));
    cout<<"Addr of p  = "<<p <<endl;
    cout<<"Value of p = "<<*p <<endl;
    free(p);
    cout<<"After free(p)"<<endl;
    r = (int*)malloc(sizeof(int));
    cout<<"Addr of r  = "<<r <<endl;
    cout<<"Value of r = "<<*r <<endl;
    *p = 100;
    cout<<"Value of p = "<<*p <<endl;
    cout<<"Value of r = "<<*r <<endl;
    return 0;
}

:

Addr of p  = 0x2f7630
Value of p = 3111728
free(p)
Addr of r  = 0x2f7630
Value of r = 3111728
*p = 100
Value of p = 100
Value of r = 100
在上面的代码中,pr是动态创建的。创建并释放pr是在p释放后创建的。在改变p中的值时,r的值也会改变。但是我已经释放了p的内存,那么为什么在改变p的值时,r的值也被修改为与p相同的值?我得出了以下结论。如果我是对的,请评论一下。

解释:指针变量pq是动态声明的。初始存储垃圾值。指针变量p释放/删除声明了另一个指针变量rr分配的地址与p的地址相同(p仍然指向旧地址)。现在,如果p的值被修改,r的值也被修改为与p的值相同的值(因为两个变量都指向相同的地址)。操作符free()仅从指针变量中释放内存地址,并将该地址返回给操作系统以供重用,但指针变量(在本例中为p)仍然指向相同的旧地址。

free()函数和delete运算符不改变指针的内容,因为指针是按值传递的。

但是,使用free()delete后,指针所指向位置的东西可能不可用。

如果我们有内存位置0x1000:

       +-----------------+  
0x1000 |                 |  
       | stuff in memory |  
       |                 |  
       +-----------------+  

假设指针变量p包含0x1000, 或指向内存位置0x1000

在调用free(p)之后,允许操作系统重用0x1000处的内存。它可能不会立即使用它,也可能将内存分配给另一个进程、任务或程序。

但是,变量p没有改变,所以它仍然指向内存区域。在这种情况下,变量p仍然有一个值,但是不应该解引用(使用内存),因为您不再拥有内存

你的分析在某些方面表面上很接近,但并不正确。

prmain()的第一条语句中被定义为指针。它们不是动态创建的。它们被定义为与main()一起自动存储持续时间的变量,因此当(在您的程序中,实际上是if) main()返回时,它们将停止存在。

创建和释放的不是pmalloc()动态分配内存,如果成功,返回一个标识动态分配内存的指针(如果动态分配失败,则返回一个NULL指针),但不初始化内存。malloc()返回的值(在c++中需要转换为指向int的指针后)被赋值给p

你的代码然后打印p的值。

(我用斜体突出显示了下一段,因为我将在下面提到它)

下一条语句输出*p的值。这样做意味着访问p所指向的地址的值。然而,该内存是未初始化的,因此访问*p的结果是未定义的行为。在您的实现(编译器和库)中,此时恰好会产生一个"垃圾值",然后将其打印出来。然而,这种行为并不能保证——它实际上可以做任何事情。不同的实现可能会产生不同的结果,例如异常终止(程序崩溃),重新格式化硬盘驱动器,或者(在实践中明显不太可能)通过计算机的扬声器播放Primitives的歌曲"crash"。

在调用free(p)之后,代码将使用指针r执行类似的序列。

赋值*p = 100具有未定义的行为,因为p保存了第一个malloc()调用返回的值,但该值已传递给free()。因此,就您的程序而言,内存不再保证存在。

之后的第一个cout语句访问*p。由于p不再存在(已被传递给free()),这给出了未定义的行为。

之后的第二个cout语句访问*r。该操作具有未定义的行为,原因与我在上面斜体段落中描述的完全相同(对于p,就像当时一样)。

但是,请注意,在您的代码中有五次出现未定义的行为。即使只出现一个未定义行为的实例,也无法预测程序的行为。通过您的实现,结果恰好是打印具有相同值的pr(因为malloc()在两种情况下都返回相同的值0x2f7630),在两种情况下打印垃圾值,然后(在语句*p = 100之后)在打印*p*r时打印100的值。

然而,这些结果都不能保证。没有保证的原因是,c++标准中"未定义行为"的含义是,该标准对允许的行为没有任何限制,因此实现可以自由地做任何事情。对于特定的实现,在编译、链接和运行代码的特定时间,您的分析可能是正确的。它甚至可能下周是正确的,但从现在起一个月后更新你的标准库(例如应用错误修复)是不正确的。对于其他实现可能不正确。

最后,还有几个小问题。

首先,你的代码是不完整的,甚至不能以你所描述的形式编译。在上面的讨论中,我假设您的代码实际上以

开头。
#include <iostream>
#include <cstdlib>
using namespace std;

第二,malloc()free()是标准库中的函数。它们不是运算符。

你对实际情况的分析是正确的;然而,程序不能保证以这种方式可靠地运行。每次在free(p)之后使用p都会"引发未定义的行为"。(当您访问*p*r而没有事先写任何内容时,也会发生这种情况。)未定义行为比仅仅产生不可预测的结果更糟糕,比仅仅潜在地导致程序崩溃更糟糕,因为编译器被明确地允许假设引发未定义行为的代码永远不会执行。例如,编译器将您的程序视为与

相同是有效的。
int main() {}

因为在你的程序中没有不引起未定义行为的控制流路径,所以一定是程序永远不会运行的情况!

free()释放堆内存供操作系统重用。但是存在于内存地址中的内容不会被擦除/移除