为什么 return 局部变量地址会崩溃?函数栈帧的真相
为什么 return 局部变量地址会崩溃函数栈帧的真相先看这段代码int*getNumber(){intn42;returnn;}intmain(){int*pgetNumber();printf(%d\n,*p);return0;}拿给一个刚学 C 的朋友看他可能会觉着没问题——getNumber 返回 n 的地址main 里用那个地址打印 42有什么不对实际跑起来第一次可能出 42第二次也可能出 42第三次也可能出 42。然后你换了一个编译器、加了一行代码、改了一个优化级别——突然就输出了垃圾值或者直接段错误。行为不一致是最恶心的那种 bug。问题出在函数返回到哪一刻。getNumber 被调用的时候系统会在栈上给它的局部变量分配空间。n 就在这片空间里地址是n值 42。getNumber 执行到 return 时栈上分配给 getNumber 的那片内存被标记为空闲。系统接下来调用 printf——printf 自己的局部变量要放到栈上——就会覆盖刚才被释放的位置。栈上的数据被推平了。p 指向的那块内存现在已经不是 n 了是 printf 内部的某个变量。所以 *p 的值变成一个随机的、属于 printf 内部的东西。每个函数都有自己的栈帧。函数返回栈帧销毁。你留着一个指向已销毁栈帧的指针跟握着一把已经失效的钥匙没什么区别——锁还是那把锁但门后面的房间已经不属于你了。很多人会说“但我跑了好几次都正常啊。”这恰恰是它最坑的地方。刚返回来的时候栈上的数据还没被覆盖所以第一次读*p可能还是 42。但如果你在后面多调用几个函数、多压几次栈——读出来的值就不可预测了。int*pgetNumber();printf(hello\n);// 这行调用了 printf压了栈printf(%d\n,*p);// 这行很可能已经不是 42 了多写一个printf结果就从对变成错。你排查的时候盯着getNumber看了半天完全没注意到问题出在调用侧。正确的做法有两种。一种是让 n 活到函数外面——用静态存储期int*getNumber(){staticintn42;returnn;}static 让 n 不在栈上分配而在全局数据区。程序不结束它就一直在。不过副作用是全局只有一个 n下次调用它就改了。另一种是在堆上分配int*getNumber(){int*pmalloc(sizeof(int));*p42;returnp;}堆上的内存在你主动 free 之前一直有效。代价是调用方必须记得 free。这两种方式本质都是让那块内存在函数返回后不被销毁。栈上的东西出了函数就没了别指望还能用。你有没有写过返回局部变量指针的代码调试了多久才发现问题来评论区聊聊。#c语言 #算法 #数据结构