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

为何C语言nop函数存在副作用?调用后*box->value值异常变更

为什么调用nop()会改变*box->value的值?

这是典型的**悬空指针(dangling pointer)**引发的未定义行为,咱们一步步拆解来搞懂问题根源:

先看完整代码

#include <stdio.h>
#include <stdlib.h>

struct Box {
    int **value;
};

void nop(void) {
    /* Why does this function have side effects? */
    void *a = malloc(sizeof *a);
    free(a);
}

struct Box *makeBox(void) {
    int *value = NULL;
    struct Box *box = malloc(sizeof *box);
    box->value = &value;
    return box;
}

int main(void) {
    struct Box *box = makeBox();
    printf("Dereferenced: %p\n", *box->value);
    nop();
    printf("Dereferenced: %p\n", *box->value);
}

核心问题分析

  1. 悬空指针的诞生
    在makeBox()函数里,value是一个栈上的局部变量(int*类型)。当makeBox()执行完成返回时,它的栈帧会被销毁,栈上的value变量所在的内存区域会被标记为可复用。但box->value被赋值为&value——也就是这个已经失效的局部变量的地址,这就导致box->value变成了一个悬空指针:它指向的内存不再属于我们的有效变量,后续对这块内存的访问都是未定义行为。

  2. nop()的“间接副作用”

    • 第一次打印*box->value时,makeBox()刚返回,栈帧还没被其他操作覆盖,所以*box->value还是原来的NULL,输出(nil)。
    • 调用nop()时,nop()的栈帧会占用原来makeBox()栈帧的部分空间(具体来说,nop()里的局部变量a的位置刚好和makeBox()里的value变量在栈上的位置重叠了)。
    • nop()里的malloc会返回一个堆内存地址,这个地址被存在栈变量a里;free(a)只是释放堆内存,但a本身(栈上的变量)存储的地址值并不会被清空。
    • 当nop()返回后,栈帧恢复,但原来value所在的栈位置已经被a修改成了malloc返回的地址。此时*box->value访问的就是这块被复用的栈内存,所以输出了那个堆地址。
  3. 注释nop()后的表现
    当注释掉nop()时,没有其他函数调用覆盖原来的栈帧,那块内存的值暂时还没被修改,所以两次打印都是(nil)——但这依然是未定义行为,只是刚好表现得符合预期而已,换个编译器或运行环境可能会有不同结果。

总结

nop()本身并没有直接修改box->value,但它的栈帧复用了makeBox()里局部变量的栈空间,而box->value指向的是这块已经失效的栈内存,所以看起来像是nop()产生了副作用。问题的本质是悬空指针导致的未定义行为。

内容的提问来源于stack exchange,提问作者Chiru

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:48:07