为了理解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;
}
答案 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 ++程序中创建变量。
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;仍然是空白,它从未设置,但由于它的全局/静态,它保持在链接器将其写入磁盘时的任何内容。