尝试使用free()来理解它是如何工作的

时间:2014-04-14 05:19:45

标签: c debugging pointers malloc free

为了理解C编程语言中free的用法,我尝试在Ubuntu上运行此代码,但是在运行EXE文件时,我收到了SIGABRT错误。为什么程序没有正常退出?

#include<stdio.h>
#include<stdlib.h>

int main()
{
   int ret;
   int *ptr;
   ptr = (int *)malloc(sizeof(int)*10);
   free(ptr);
   ptr = &ret;
   free(ptr);
   return 0;
}

7 个答案:

答案 0 :(得分:38)

尝试释放未从malloc(或其中一位朋友)获取的指针会导致未定义的行为。您的第二次free(ptr)来电尝试就是这样。

根据C规范,§7.22.3.3free函数,第2段:

  

free 函数会导致 ptr 指向的空格被释放,即可用于进一步分配。如果 ptr 是空指针,则不会执行任何操作。否则,如果参数与内存管理函数先前返回的指针不匹配,或者通过调用 free realloc取消分配空间,行为未定义。

答案 1 :(得分:11)

   ptr = &ret;
   free(ptr);

与:

相同
    free(&ret);

ret驻留在堆栈上,而不是堆上。尝试在堆上未分配free()内存(使用malloc()等)将导致未定义的行为。

答案 2 :(得分:11)

虽然所有未定义的行为答案都是正确的,但看到实现可能会更有帮助。

我认为对此的一个很好的参考是K&R C malloc。关于它如何工作的解释在"The C Programming Langugage",第8.7章。

请记住,在C语言中查看来自标准库函数的代码时,它们具有实现和规范,其中实现可能具有其规范不需要的可重现行为。

基本上几乎所有malloc实现都有一个空闲列表,用于管理列表(或多个列表)中的空闲内存区域。自由列表通常以某种方式处理,多次在内存区域上调用free会使此列表处于不正确的状态。

当有目的地制作数据结构时,也可以调用恶意行为。 Phrack有一篇关于如何在破坏freelist时将无效内存传递给free时调用代码执行的文章。

Other malloc implementations也可能值得关注(这是一个不完整的分配库列表)。

答案 3 :(得分:9)

函数free()仅适用于由heap malloc()分配的内存。不适用于静态分配,因为静态分配是自动处理的。这是错误:

 int ret; 

 ptr = &ret;

 free(ptr);

您无法执行此操作,因为ret的内存未在堆上分配。它位于堆栈中,只应释放堆的内存。

答案 4 :(得分:8)

当您尝试释放未分配的指针时,您的第二个free(ptr)导致未定义的行为。另请注意,静态分配会自动回收,因此您不需要它们来释放它。

答案 5 :(得分:5)

  ptr = &ret;
   free(ptr);

在这里,您尝试释放本地存储堆栈变量的内存。根据规则,你不应该释放它。当main()退出堆栈上的所有本地存储内存时,总是获得空闲。

免费仅适用于堆分配内存。

答案 6 :(得分:2)

有三个区域可以在c或c ++程序中创建变量。

  • 全局变量或静态变量位于可执行二进制文件中的固定位置。
  • 静态范围之外的自动变量在堆栈上
  • malloc&#39; ed或calloc&#39; ed变量在堆上。

free()是释放堆上先前分配的内存的函数。 malloc或类似函数返回指向堆上内存的指针。读取或写入该内存的唯一方法是通过指针。指针是地址,指针*是指向该地址的内容。

在所示的示例中,有两个变量,在Main中定义,实际上是静态的,而Main中的返回值在堆栈中。使用miniumum int,16位,这里是一个可能的存储器映射。在这个映射中,指令从0开始,堆栈从某个非零值开始(堆栈开始 - bos)并通过递增增长,堆从最大地址开始(... FFFF,又称-1)通过减少而增长:

(记住,MIN_INT是-32768,MAX_INT是32767 ......规范只保证16位,签名)

每个字节的地址都是&#39; n&#39;位宽 - 16位,32位或64位,通常


-1。 (堆的开始,例如,16位地址:0xFFFF,32位地址:0xFFFFFFFF或64位地址:0xFFFFFFFFFFFFFFFF)

-2。 (从堆开始的第一个位置.0x ... FFFE)ptr [9],一次

-3。 (从堆开始的第二个位置.0x ... FFFD)

-4。 (从堆开始的第三个位置.0x ... FFFC)ptr [8],一次

[剪断]

-17。 (从堆开始的第16个位置.0x ... FFEF)

-18。 (从堆开始的第17个位置.0x ... FFEE)ptr [1],一次

-19。 (从堆开始的第18个位置.0x ... FFED)

-20(从堆开始的第19个位置.0x ... FFEC)ptr [0],一次

-21。 (堆顶部,从堆的开头向下10 X 16位整数.0x ... FFEB),一次

: 32或64位机器上的大范围地址...... :

:(堆栈顶部0x ... tos)

bos +(sizeof(int) - 1)从Main()

返回的int结束

bos :(堆栈开头:静态数据之上)从Mail()

返回int的开始

togs :(全局/静态的顶部)结束&#34; ptr&#34;

:(指针的大小是地址总线的宽度......无论如何)

togs-(n-1):( top / global - (sizeof(int *) - 1))&#34; ptr&#34;

的开头

(togs-n):结束&#34; ret&#34;

(togs -n)-1:&#34; ret&#34;

的开始

(编译器为自己添加的任何全局内容,调试器等)

(程序代码结束)

(程序代码的开始)

(非程序代码的顶部)

0(非程序代码的开始,0x ... 0000)


在运行时,&#34; ptr&#34;并且&#34; ret&#34;可能都是从&#39; 0&#39;开始,因为它们是固定的,静态的,值,从可执行二进制文件来自文件中读出。

随着程序的运行,&#34; ptr&#34;更改,首先,指向堆,在10个整数的malloc数组:&#34; 0x ... FFEC&#34;

对free()的调用不会改变ptr的值,它仍然是&#34; 0x ... FFEC&#34; 免费&#34; 0x ... FFEC&#34;是合法的,没有任何有趣的事情。

作业&#34; ptr =&amp; ret&#34;将新值设置为&#34; ptr&#34;,&#34;(togs-n)-1&#34;,&#34; ret&#34;的开头。

免费&#34;(togs-n)-1&#34;导致立即崩溃,因为&#34;免费&#34;检查&#34;(togs-n)-1&#34;的值并且它不在堆地址的有效范围内。

&#34;保留&#34;仍然是空白,它从未设置,但由于它的全局/静态,它保持在链接器将其写入磁盘时的任何内容。