
代码核心
- fun函数执行完毕函数栈帧销毁:局部变量a的内存空间被回收,这块栈内存已经不属于a了
- 指针p保存的仍然是原来a的地址,但是地址指向的内存已经失效 → p就是悬空指针
- 读取*p属于未定义行为 UB,结果不确定
为什么第一次输出 10,第二次变成乱码
● 调用fun结束,栈帧销毁,只是标记这块内存可以被复用,不会立刻清零
● 第一次printf("%d\n", *p);:此时栈内存还没被别的指令覆盖,数据10还留在原地,所以碰巧打印 10
● 执行完第一个printf,printf函数调用会复用这块栈空间,把栈内存覆盖
● 第二个printf("%d\n", *p);:原来存放 10 的内存已经被改写,读出随机垃圾值 -858993460
重点:不是一定会先对后错! 未定义行为,不同编译器、不同编译选项、不同系统,结果完全不一样,有可能两次都是 10,也有可能直接程序崩溃
悬空指针定义
指针保存了一个合法地址,但这个地址对应的内存已经被释放 / 销毁,这个指针就叫悬空指针
区别:
● NULL 指针:指针 = 0,指向空,没有指向任何内存
● 悬空指针:指针有地址值,但指向的内存已经失效
产生悬空指针常见场景
- 函数返回局部变量的地址(当前这个例子)✅
- free释放堆内存之后,没有把指针置 NULL
c
int *p = malloc(sizeof(int));
free(p); // 内存释放
// p现在就是悬空指针!p里面地址没变,但内存已经还给系统
- 数组 / 内存空间生命周期结束,指针还保留旧地址
怎么避免悬空指针
- ❌ 不要返回栈上局部变量的地址;想返回数据,可以传指针参数、返回堆 malloc 内存(记得 free)、static 静态变量(static 变量不在栈)
- free之后,手动把指针赋值为NULL
c
free(p);
p = NULL;
- 保证指针生命周期 ≤ 指向内存的生命周期
补充总结
| 项目 | 说明 |
|---|---|
| 局部变量 a | 在 fun 函数栈帧,函数 return 后栈帧销毁,内存回收 |
| p 指针 | 保存 a 原来的地址,内存失效 → 悬空指针 |
| *p 解引用 | C 标准:未定义行为,结果随机,程序可能崩溃 |
| 现象原因 | 栈不会自动清零,后续函数调用会覆盖栈内存 |