free()运算符是否从动态变量中删除地址?

时间:2016-08-13 22:00:11

标签: c++ heap dynamic-allocation

让我们考虑下面的程序:

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

在上面的代码中,动态创建 p r p 已创建并已释放。在 p 被释放后创建 r 。 在更改 p 中的值时, r 的值也会更改。但是我已经释放了 p 的内存,那么为什么在更改 p 的值时, r 的值也会被修改为相同的价值如 p

我得出了以下结论。如果我是对的,请评论?

说明: 动态声明指针变量 p q 。最初存储垃圾值。指针变量 p 已释放/已删除。声明了另一个指针变量 r 。分配给 r 的地址与 p 的地址相同( p仍然指向旧地址)。现在,如果 p 的值被修改, r 的值也会被修改为与 p 相同的值(因为两个变量都是指向同一地址)。 运算符 free()仅从指针变量释放内存地址并将地址返回给操作系统以供重用,但指针变量( p 在这种情况下) )仍然指向同一个旧地址。

4 个答案:

答案 0 :(得分:3)

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

但是,使用free()delete后,指针指向的位置中的内容可能无法使用。

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

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

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

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

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

答案 1 :(得分:3)

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

pr被定义为main()的第一个语句中的指针。不是动态创建的。它们被定义为main()的自动存储持续时间的变量,因此当(实际上如果,在程序的情况下)main()返回时,它们就不再存在。

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

然后您的代码会打印p

的值

(我已经用斜体突出显示了下一个段落,因为我将在下面回复它。)

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

调用free(p)后,您的代码会使用指针r进行类似的序列。

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

之后的第一个cout语句访问*p。由于p不再存在(已传递给free()),因此会产生未定义的行为。

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

但请注意,代码中出现了五次未定义的行为。即使发生了一个未定义行为的单个实例,所有的赌注都会因为能够预测程序的行为而被取消。通过实现,结果恰好是打印pr具有相同的值(因为malloc()在两种情况下都返回相同的值0x2f7630),打印垃圾值在这两种情况下,然后(在声明*p = 100之后)在打印100*p时打印*r的值。

但是,这些结果都不能得到保证。不能保证的原因是&#34;未定义的行为的含义&#34;在C ++标准中,标准没有对允许的内容进行任何限制,因此实现可以自由地执行任何操作。对于特定的实现,在编译,链接和运行代码的特定时间,您的分析可能是正确的。下周甚至可能是正确的,但是在更新标准库之后一个月内不正确(例如应用错误修复)。其他实现可能不正确。

最后,有几个小问题。

首先,您的代码不完整,甚至无法以您描述的形式进行编译。在上面的讨论中,我假设您的代码实际上以

开头
#include <iostream>
#include <cstdlib>

using namespace std;

其次,malloc()free()是标准库中的函数。他们不是运营商。

答案 2 :(得分:2)

您对实际情况的分析是正确的;但是,程序保证可靠地表现这种方式。在free(p)&#34;之后*p的每次使用都会引发未定义的行为&#34;。 (当您在没有先写入任何内容的情况下访问*rint main() {} 时也会发生这种情况。)未定义的行为更糟而不仅仅是产生不可预测的结果,并且更糟< / em>而不仅仅是可能导致程序崩溃,因为显式允许编译器假定引发未定义行为的代码永远不会执行。例如,编译器将您的程序视为与

相同是有效的
.desktop

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

答案 3 :(得分:0)

free()释放堆内存以供OS重用。但是存储器地址中存在的内容不会被删除/删除。