最近看 Linux 内核,看到一个问题: malloc 申请的内存,指针为 prt, free 之后如果没有赋值 prt=NULL 会如何?

2015-04-05 02:28:16 +08:00
 wind3110991

例如:
在堆段中手动malloc申请一段内存:
char* ptr = (char*)malloc(10);
........
free(ptr);
//ptr = NULL;假设这步忘记做了

那么这个ptr指针是不是依然指向那一块内存?
也就是说我接下来下次依然可以使用ptr指针?
貌似free的内存块是要够一页大小才会置换页面的
请问这个ptr如果没有赋值NULL会有什么后果?

另外,那么问题又来了
如果我双重释放ptr又会如何?
free(ptr);
free(ptr);

1681 次点击
所在节点    C
21 条回复
ryd994
2015-04-05 03:05:24 +08:00
依然指向
用的话可能segfault
重复free的话会segfault
赋值null就是为了避免重复free
free NULL是安全的
ncisoft
2015-04-05 03:17:11 +08:00
有这闲功夫发帖,都够写段代码测试下了,差评
RobberPhex
2015-04-05 11:17:17 +08:00
glibc是不允许多次free的,会出现异常:`Error in './a.out': double free or corruption`

另外,free小块内存后是可以访问的,因为那块内存还是被glibc管理的,不过这会破坏glibc的内存管理机制。

不过,释放大块内存的时候,glibc会将其归还给操作系统,此时访问会导致段错误。
//我猜测:mmap分配的内存,释放时会直接归还操作系统;brk/sbrk在顶端释放大量内存时会归还。
wind3110991
2015-04-05 12:21:34 +08:00
@ncisoft 谢谢
wind3110991
2015-04-05 12:30:57 +08:00
@RobberPhex 就是说free的对象要看是大还是小吧?谢谢你!你解答了我之前的一个问题,就是free之后的内存会不会马上归还给os
phoeagon
2015-04-05 12:35:48 +08:00
@wind3110991 还不还是implementation specific,不是标准定的,不要依赖编译器这种随时能改的实现
wind3110991
2015-04-05 16:06:25 +08:00
@phoeagon = =不太理解,就是说还是要以内核工作机制为准吗
msg7086
2015-04-05 16:36:16 +08:00
@wind3110991 就是说,一旦free了,这块内存就不属于你了。
接下来的访问就相当于偷着用,抓到就segfault,没抓到就是凑巧。
就像你租房期满,钥匙还给了房东。
接下来你再拿偷配的钥匙访问房子就可能会被抓。
gamexg
2015-04-05 22:11:12 +08:00
还有可能破化程序的其他部分。

你已经 free 了,其他代码 malloc 时有可能分配到相同位置,然后保存数据。
jedihy
2015-04-05 23:15:03 +08:00
因为后面用这个变量了吧,节省一条指令
wind3110991
2015-04-07 00:30:05 +08:00
@gamexg 谢啦~~我明白啦
khan
2015-04-30 10:43:43 +08:00
因为有很多地方都是这样判断

if (ptr == NULL) {
...do something
}

你如果不置空, 后面打算怎么复用.


free(ptr);
ptr = NULL; //如果不打算复用指针, 这行代码意义不大. 也没有危险或潜在威胁

所以这种代码 称之为防御性代码.
khan
2015-04-30 10:49:50 +08:00
@RobberPhex: 你说的内容有俩疑点
1. c++ 中我知道有new alloc 内存复用机制, 可以由 stdlib 来管理代码分配的内存.
但是 c 中的 alloc 就真的是直接调用系统的内存管理模块了. 应该不存在你说的 libc 管理内存的可能性.

2. mmap 是内存镜像文件. 我记得没错的话是处理超大型文件时不载入内存的一种实现方式. 直接用磁盘I/O来进行文件操作. 大文件载入内存导致爆内存的问题. 所以 mmap 本身应该可以认为是不占用内存的.

错漏之处还望斧正.
RobberPhex
2015-04-30 12:36:05 +08:00
@khan malloc是libc提供的,在glibc的实现中,是有glibc自己的内存池的。另外,alloc应该也是libc提供的功能,但是linux没有这个内存调用;只有brk调用。

至于mmap,glibc在分配大块内存时也会用到,在逻辑上占用了内存,但是实际上在读写后,发生缺页中断后才会真正分配。
khan
2015-04-30 13:58:24 +08:00
@ryd994 这种情况其实称之为野指针. 情况不可预估. 可能你会发现某个堆的数据突然变化了. 从而导致各种灵异现象.
另外如果 = NULL, 是空指针, 和内核地址重叠. 如果强制访问. 持有进程会被内核杀死.
ryd994
2015-04-30 14:05:32 +08:00
@khan 所以我说可能Segfault。主要看这个地址是否还属于这个进程。
访问空指针当然是要死,但是free是可以的。
khan
2015-04-30 15:00:16 +08:00
@ryd994 32bit 保护地址模式早就没有远程指针的概念了. 无法访问不属于本进程的地址吧
ryd994
2015-04-30 15:04:38 +08:00
@khan 是的
所以如果运气好,尽管调用了系统调用,申请释放,但系统还没有释放(有性能因素不是立刻释放),那么地址还属于这个进程,则会出现访问混乱。
如果操作系统已经释放了内存,就会segfault
khan
2015-04-30 15:09:44 +08:00
@ryd994 在@RobberPhex的提示下, 正在看Ptmalloc2的 Free 部分实现. 证实了你的说法.
qiuyi116
2015-05-27 22:55:42 +08:00
free但是不给赋NULL就是自己挖坑,,亲测啊。。因为free知识把ptr指向的内存资源释放归还给了OS,但是呢,ptr还是指向这个虚拟地址的啊。访问ptr的值就会Segmentfault.....

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/181639

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX