为了理解free在C编程语言中的用法,我尝试在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;
}
试图释放未从malloc
(或其朋友)获得的指针会导致未定义的行为。您的第二个free(ptr)
呼叫正是这样尝试的。
根据C规范,§7.22.3.3 free
函数,第2段:
free
函数会解除分配ptr
指向的空间,也就是说,使其可用于进一步分配。如果ptr
是空指针,则不会执行任何操作。否则,如果参数与内存管理函数先前返回的指针不匹配,或者调用free
或realloc
释放了空间,则行为未定义。
虽然所有未定义的行为答案都是正确的,但查看实现可能会更有帮助。
我认为一个很好的参考是K&R C malloc。关于它的工作原理,请参阅"C编程语言",第8.7章。
请记住,当查看C中标准库函数的代码时,它们有一个实现和规范,其中实现可能具有规范不要求的可复制行为。
本质上,几乎所有的malloc
实现都有一个空闲列表,它们在其中管理一个列表(或多个列表)中的空闲内存区域。空闲列表的处理方式通常是,在内存区域上多次调用free
会使该列表处于不正确的状态。
当有目的地构建数据结构时,也可能会调用恶意行为。Phrack有一篇过时的文章,介绍了如何在损坏自由列表的同时向free
传递无效内存时调用代码执行。
其他malloc实现可能也值得一看(这是一个不完整的分配库列表)。
ptr = &ret;
free(ptr);
与相同
free(&ret);
ret
驻留在堆栈上,而不是堆上。尝试在堆上分配未分配的free()
内存(使用malloc()
等)将导致未定义的行为。
函数free()
仅适用于malloc()
在heap
上分配的内存。不适用于静态分配,因为静态分配是自动处理的。以下是错误:
int ret;
ptr = &ret;
free(ptr);
您不能这样做,因为ret
的内存没有在堆上分配。它在堆栈中,应该只释放堆的内存。
当您试图释放未分配的指针时,您的第二个free(ptr)
会导致未定义的行为。还要注意,静态分配是自动回收的,所以不需要它们来释放它。
ptr = &ret;
free(ptr);
在这里,您正在尝试释放本地存储堆栈变量的内存。根据规则,你不应该释放它。当main()退出时,堆栈上的所有本地存储内存都会释放。
Free仅适用于堆分配内存。
在c或c++程序中有三个区域可以创建变量。
- 全局或静态变量位于可执行二进制文件中的固定位置
- 静态作用域之外的自动变量在堆栈上
- malloc'ed或calloc'ed变量在堆中
free()是释放堆上先前分配的内存的函数。指向堆上内存的指针由malloc或类似函数返回。读取或写入内存的唯一方法是通过指针。指针是一个地址,指针*是指向该地址的内容。
在所示的示例中,有两个变量,在Main中定义,实际上是静态的,还有Main的返回值,它在堆栈上。使用16位的最小整数,这里有一个可能的内存映射。在这个映射中,指令从0开始,堆栈从某个非零值开始(堆栈-bos的开始),并通过递增而增长,堆从最大地址开始(…FFFF,又名-1),并以递减方式增长:
(记住,MIN_INT是-32768,MAX_INT是32767…规范保证只有16位,有符号)
每个字节都有一个"n"位宽的地址——16、32或64位,通常为
-1.(堆的开始,例如,16位地址:0xFFFF、32位地址:0xFFFFFFFF或64位地址:0x%FFFFFFFFFFFFF)
-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位机器上的地址范围非常大。。。:
tos:(堆栈顶部0x…tos)
bos+(sizeof(int)-1)从Main()返回的int结束
bos:(堆栈的开始:在静态数据之上)从Mail()返回的int的开始
togs:(全局/静态顶部)"ptr"结束
:(指针的大小是地址总线的宽度……不管需要什么)
togs-(n-1):(全局/静态的顶部-(sizeof(int*)-1))"ptr"的开始
(togs-n):"ret"结束
(togs-n)-1:"ret"的启动
(编译器为自己、调试器等添加的任何全局内容)
(程序代码结束)
(程序代码开始)
(非程序代码顶部)
0(非程序代码的开始,0x…0000)
在运行时,"ptr"one_answers"ret"可能都从"0"开始,因为它们是固定的、静态的值,是从可执行二进制文件中读取的。
当程序运行时,"ptr"的值会发生变化,首先,指向堆中的malloc’ed数组中的10个整数:"0x…FFEC"
对free()的调用不会更改ptr的值,它仍然是"0x…FFEC"释放"0x…FFEC"是合法的,运行时没有任何有趣的东西。
赋值"ptr=&ret"将一个新值设置为"ptr","(togs-n)-1",即"ret"的开头。
释放"(togs-n)-1"会导致立即崩溃,因为"Free"检查"(togst-n)-1)"的值,而它不在堆地址的有效范围内。
"ret"保持空白,它从未设置过,但由于它是全局/静态的,所以它保持在链接器将其写入磁盘时的状态。