You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:42:54