从一段野指针代码,看懂内存与指针的关系

张开发
2026/6/9 1:06:59 15 分钟阅读
从一段野指针代码,看懂内存与指针的关系
先看一下当前代码的运行结果为什么?为什么会是这个结果#include iostream using namespace std; int* test1() { int a 10; int* q a; return q; } int main() { int* p test1(); cout *p endl; return 0; }运行后会发现输出结果竟然是 10。明明a是局部变量函数结束就应该被销毁了为什么还能正常读到值这篇文章不从语法表面讲而是从内存本质彻底理解。一、先明确结论这是典型的悬空指针野指针a是函数内的局部变量存储在栈上。当test1()执行结束函数栈帧销毁这块内存就被标记为可复用。此时p拿到的是一块已经不属于它的地址属于未定义行为程序随时可能崩溃或出现乱码。但这不是本文重点。我们真正要弄明白的是既然已经销毁了为什么还能输出 10二、内存的本质只是一段可以读写的空间内存本身没有 “所有权”“权限”“销毁” 这些概念。它就是一串字节只要你知道地址理论上就能访问。所谓“变量销毁”本质只有一件事栈指针回退标记这块内存为可被覆盖并不会把数据清零也就是说内存里的10还在地址依然有效只是系统不再保证它不会被覆盖你依然可以通过指针访问它内存不会拦着你。三、指针的真正作用不是 “创造内存”而是 “占坑”我在初学时一直以为指针在 “维护使用内存”。但实际上用局部变量、new开辟内存 → 相当于占坑告诉系统和编译器这块我在用别分给别人函数返回、delete→ 放弃占坑标记为可复用野指针访问 → 没有占坑但强行使用这块内存所以内存本来就是拿来用的关键是你有没有合法使用权。野指针就是 “没有使用权但硬要访问”。四、为什么本例中还能读到 10因为函数返回后还没有任何操作覆盖这块栈内存。数据只是被 “释放”但没有被 “擦除”。就像你退房离开房间东西还在只是下一个住户随时会进来换掉。一旦再调用一个函数栈内存被复用值立刻就变void test2() { int x 999; } int main() { int* p test1(); test2(); // 假设精准覆盖p指向的栈空间 cout *p endl; // 不再是10 }五、总结一句话内存本质是公共空间谁拿到地址都能访问。指针的意义是占坑声明这块内存不可被系统随意分配。局部变量返回地址 放弃占坑但地址还能用。能读到 10 只是因为数据还没被覆盖属于侥幸不是正确行为。这种行为属于未定义行为非法且危险绝不应该在实际代码中使用。

更多文章