免费不会删除分配给指针(int数组)的内存,使用两次免费可以起作用,为什么?

4

出于好奇,我试图在之前的问题中找到答案,但似乎没有答案。所以在这里询问,我刚刚编写了一段代码,尝试为int指针(用于填充数组)分配内存并将int值扫描到其中。完成数组后,我想删除分配给指针的数据/内存。虽然我的代码运行良好,但是即使在释放指针后,我仍然能够看到int数组中的值。而如果我使用free两次,它会抛出错误(预计在单次使用free后)。这不是一种奇怪的行为吗?我在我的Mac上使用Xcode和Codechef进行了尝试,得到了相同的结果,为什么?

int *num=(int *)malloc(n*sizeof(int));
int i;
for(i=0;i<n;i++)
{
    scanf("%d",&num[i]);        
}    

for(i=0;i<n-1;i++){
    temp = some_function(x);        
}

free(num); 

for(i=0;i<n;i++)
{
    printf("\nnum[%d]= %d\n",i,num[i]);        
}

上述代码打印了num[]中的值,这实际上是不应该的。

4
这是一种未定义行为,任何事情都可能发生,包括正常工作。通常 free 不会真正清除那块内存,而只是将其释放以供其他分配使用。 - PcAF
因为 free 必须释放给指针分配的内存。 - CodeInfinity
你的编辑实际上是第二个问题,请单独发布。 - Tom Tanner
完成,一旦人们开始查看新问题,将删除_EDIT_。 - CodeInfinity
4个回答

10
这并不奇怪或错误。在调用free指针时,你只是告诉内存管理器可以再次重复使用该内存部分进行另一个malloc调用。它不会擦除内容(这将需要额外的时间且没有任何功能)。但是,你可以访问它,因为指针仍然指向有效地址。尽管如此,它会导致未定义行为,因为内存管理器可能已将该内存分配给其他代码。
第二次调用free可能会生成错误,因为内存管理器不再将该指针视为有效指针(你刚刚释放了它!)。事实上,这是未定义行为

好的,你的意思是第一次使用“free”,然后访问数组元素并没有导致_未定义行为_,而使用两次后再访问数组元素则会导致未定义行为。无论如何,理想情况下不应该出现这种情况,最好的方法是如何防止这种情况发生?也就是说,如果另一个用户尝试访问过期数据,如何防止这种情况发生? - CodeInfinity
@CodeInfinity 访问已释放的内存会导致未定义行为。 "未定义"并不意味着它会抛出错误或崩溃。 - Bart Friederichs
@CodeInfinity "另一个用户" 如何尝试访问那些数据?您可以通过在应用程序中进行良好的内存管理或转向自动完成此操作的语言(例如Java,C#)来防止发生这种情况。 - Bart Friederichs
内存分配器可以分配内存,以便在“free”(Linux上的mmap/munmap)后将其返回给操作系统。 - J.J. Hakala

6

你的程序有些调皮,存在着未定义行为

第一次使用free释放内存后,内存确实已经返回给了系统,只是释放内存:

  1. 不一定清空内存
  2. 不一定阻止您访问它

再次释放会引发错误,因为您已经将其释放回系统。

这并不意味着您可以只是malloc内存,再次free,并依赖于以后仍然能够访问它!

首先,您看到的行为是未定义的 - 根据C标准,您的程序实际上是无效的。在您的特定编译器、操作系统和环境组合中,您仍然可以访问该内存的内容,但它可能会突然崩溃。此外,“未定义行为”允许编译器进行各种奇怪的事情,使后续代码根本不能按照您的期望执行。

其次,在释放后的任何时间点,该内存都可用于后续的malloc分配。因此,即使您依赖于编译器/os/环境的行为没有破坏您的使用后释放代码,各种其他进程也可能使用该空间填充其他内容。

使用后释放是一个非常严重的错误,所以只需释放一次即可,永远不要访问已释放的内存。

请参见:https://cwe.mitre.org/data/definitions/416.html以了解可能遇到的安全问题类型。


感谢您的输入。 - CodeInfinity

1
您的程序行为是未定义的。
一旦调用了free,就不应再尝试读取该内存。请注意,再次对同一指针调用free的行为也是未定义的:不要这样做!
C运行时没有理由过于热心并以某种方式禁止您明确执行正在执行的操作,因为这在编写良好的C程序中是不优秀的。

感谢您的输入。 - CodeInfinity

1
一旦调用了free(),就表示您已经告诉内存管理器您已经完成了这块内存的使用。内存管理器现在可以使用这块内存来处理新的分配请求。
如果您再次访问相同的内存位置,因为您已经有了指向该内存位置的指针,这是未定义的行为。
在相同的内存位置上再次调用free()也是未定义的行为。

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