目录
0 缘起
1 free() 使用前
- free前程序做了什么
2 free 的作用
- free对内存做了什么
- free对指针做了什么
3 free后怎样做
4 Q&A
(1) Q:有malloc 一定有free吗?
(2) Q:该free的地方没有free,会发生什么?
(3) 野指针和悬空指针?
5 讲个故事
6 参考文献
0 缘起
在回顾用C语言实现链表的过程中,发现
每个节点在开辟空间之后,到下一个节点被开辟空间之前,没有free();
只有在删除节点时才用free()
上述两点让我产生疑惑,所以决定对动态分配内存空间进行深入地理解。
1 free() 使用前
- free前程序做了什么
程序一定进行了内存空间的动态分配。 比如使用了malloc() 函数等。
malloc函数开辟指定大小的内存空间,并返回指向这段空间的起始地址的指针。
为叙述方便,这里用malloc()为例分配空间,返回的指针设为ptr.
The behavior is undefined if the value of ptr does not equal a value returned earlier by malloc(), calloc(), realloc(), or aligned_alloc() (since C11).
如果free的参数——指针,与离它最近的,具有相同变量名的,内存分配函数所返回的t指针所指向的地址不同,则可能会发生错误。
——在malloc()和free()之间,不能对地址进行修改。
2 free 的作用
- free对内存做了什么
Deallocates the space previously allocated by malloc(), calloc(),
aligned_alloc, (since C11) or realloc().[^1]
free释放了由动态分配内存函数返回的指对应的分配内存空间 。
-——free释放的是内存空间,而不是指针。
——释放,指针指向的内存空间可以被其他变量所占用,但被占用前,内存空间中的内容仍然存在。但是无法判定是否被占用
The behavior is undefined if the memory area referred to by ptr has
already been deallocated, that is, free() or realloc() has already
been called with ptr as the argument and no calls to malloc(),
calloc() or realloc() resulted in a pointer equal to ptr afterwards.[^1]
同一内存空间,在一次free后没有再次分配空间的情况下,不能再次被free。
- free对指针做了什么
指针指向的地址,在没有重新分配内存前,也没有发生变化。
If ptr is a null pointer, the function does nothing.[^1]
free的参数——指针——为空时,free函数什么也不做
3 free后怎样做
free后,指针仍然指向分配的内存空间,指针指向的地址没有变化,地址内的内容也没有发生变化。变化的是这块内存的
The behavior is undefined if after free() returns, an access is made through the pointer ptr (unless another allocation function happened to result in a pointer value equal to ptr). [^1]
在free后进行指针指向的地址访问——所谓的解引用(不是分配空间),可能出现错误。
为了防止在释放内存空间后,紧接着访问内存空间带来的错误,需要在free后,将指针指向NULL
ptr=NULL;
ptr 指向地址为 0 的内存。NULL 其实就是 0x0。此处的地址,没有访问权限。
这就提醒别人不要对这个指针进行解引用的操作。这样一来,
指针ptr就与源来的分配空间不再关联。
而变量名为ptr的指针在此后可以再次通过动态分配内存指向新的内存空间。
#include
int main(){
int *x;
x = (int*)malloc(sizeof(int));
*x = 3;
printf("%d\n ", x);
printf("%d\n", *x);
free(x);
if(x != NULL){
printf("%d\n", x);
printf("%d\n", *x);
}
x=NULL;
printf("%d\n", x);
printf("%d\n", *x);
return 0;
}
最后运行输出的结果是
29540368
3
29540368
0
0
Segmentation fault (core dumpe
解释:
通过指针释放了动态分配的内存之后,指针还是指向原来的地址,
还可以访问原来的地址(不过原来的地址中的值可能变了),
而最后将指针置为 NULL 之后,显然指针不再指向原来的地址,
而且如果这时候再想通过指针访问对应的内存,就会报段错误。[^2]
4 Q&A
(1) Q:有malloc 一定有free吗?
A:是的
但并不是说一个malloc一定要对应一个free.要看具体的场景。
一旦free,就丧失了指针对这段内存空间的占有权和使用权。
比如物理结构是链表的实现场景,链表中的各个节点存储在当时分配的内存空间中。如果在构造链表的过程中,边申请存储节点的空间边释放节点空间,那么已经建好的结点的内容有可能被抹掉,所以只要有对该链表及其元素的操作,就不能free。
(2) Q:该free的地方没有free,会发生什么?
首先,什么时候该free?ptr对应的内存空间不再需要被访问或者被修改,那么,必须free.
否则,ptr对应的内存空间将不能被其他变量使用,造成内存泄漏(Memory Leak)
(3) 野指针和悬空指针?
野指针(wild pointer)
a pointer that has not been correctly initialized and therefore points to some random piece of memory. It is a serious error to have wild pointers.[3]
只是定义了指针指向的类型,而指针没有指向内存空间。把这样的指针叫野指针。
悬空指针(dangling pointer)
If a pointer still references the original memory after it has been freed, it is called a dangling pointer.
There is nothing wrong with having a dangling pointer unless you try to access the memory location pointed at by that pointer.[3]
若两个指针(p1和p2)指向同一块内存区域,
那么free(p1)后,p1和p2都成为悬空指针。
如果进一步将p1设置为NULL, 那么p2还是悬空指针。
使用p1会导致非法内存访问,但是使用p2却会出现无法预料的结果 [3]
悬空指针的处理——智能指针()待补充
基本思路:在释放一块内存时,将指向这块内存的指针变量设置为NULL。
访问指针变量前,先判断是否为NULL。
进阶:当有多个指针变量都指向同一块内存,释放这块内存时,需要将所有指针变量的值都置为NULL,这需要维护所有指向这块内存的指针变量的信息,
但是这种方式开销大,所以通常很少使用。
可以使用引用计数,只有当引用计数为0时,才释放内存,否则,只是引用计数减1.
5 讲个故事
小明租住了西虹市A小区302房,他与房东签了6个月的租房合同,签完合同小明得到了房间的钥匙。这六个月内小明就有了302房间的占有权和使用权,他可以添置家具,邀请客人,但不能对房间搞破坏,也不能修改门牌号。
六个月过去了,小明不再租住这个房子,由于时间仓促,他没来得及把钥匙交回给房东。
302可能此后没有人来租住,房东也比较懒,没有对房内摆设进行改动。也可能改动了。但这跟小明都没有关系了。
此后,
房东可能换了把新锁,
也可能没换,
过几年A小区可能拆迁了。
那有一天小明喝醉酒,鬼使神差地,小明带着这把钥匙再去西虹市A小区302,对用以上的三种情况,分别会以下三种情况:
钥匙打不开302的门
钥匙打开了门,但门上的监控触发报警,因为私闯民宅是犯法的。
他找不到A小区302了,因为A小区已经不存在了。
6 参考文献
[1] 链接: free函数.
[2] 链接: 内存的分配与释放,内存泄漏.
[3] 链接: 野(wild)指针与悬空(dangling)指针.