C语言中多次free已释放指针未触发段错误的原因问询
内存访问未触发段错误的原因解析
int main() { void **values = calloc(10, sizeof(void *)); int *a = malloc(sizeof(int)); *a = 100; int *b = malloc(sizeof(int)); *b = 200; values[0] = a; values[1] = b; values[0] = NULL; free(values); values[1] = NULL; free(values); free(a); free(b); return 0; }
问题
为何这段代码未触发段错误?它明明访问了已释放的内存区域。虽然Valgrind能检测出详细错误,但程序运行时无报错。将calloc的参数改为calloc(1, sizeof(void *))后结果相同;但把values[1] = NULL改为values[2] = NULL(此时calloc分配10个void*)时,程序会触发段错误。
解答
先搞清楚核心一点:访问已释放内存属于C语言的「未定义行为」,段错误不是必然触发的,会不会崩溃完全看内存管理器和操作系统的实际处理逻辑,具体原因拆解如下:
- 已释放内存没被立刻回收:调用
free(values)后,内存管理器只是把这块内存标记为「可复用」,不会马上把它从进程的地址空间里删掉。这时候values还指着原来的内存地址,只要这块内存还没被重新分配出去,也没碰操作系统的内存保护规则,写values[1]就不会触发段错误。Valgrind能查出来是因为它全程盯着内存的分配释放状态,主动揪这类非法操作;而操作系统只有在你碰了完全不属于当前进程的内存时,才会抛出段错误。 - 改
calloc(1, sizeof(void*))仍不崩:哪怕只分配了1个指针的空间,values[1]属于越界访问,但只要这个越界的地址还在进程合法的地址空间里(比如内存管理器为了对齐或者批量分配,实际给的内存比你请求的大),操作系统就不会管,程序自然不会崩溃。 - 改
values[2] = NULL触发崩溃:这时候values[2]的位置超出原内存块太多,落到了进程地址空间里没被映射的区域,或者内存管理器标记为不可访问的保护页,操作系统的内存保护机制立刻就会拦截,触发段错误终止程序。
这种未定义行为绝对不能忽视,现在不崩溃不代表以后没问题,很可能在后续运行中出现随机崩溃、数据乱掉之类的诡异bug,必须按Valgrind的提示把内存错误修好。
内容的提问来源于stack exchange,提问作者shayan rok rok
相关产品推荐
相关产品推荐

