悬空指针
释放后使用的风险
悬空指针 是 CoddyKit 上的免费 C Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C Academy 课程共包含 4 节课。
什么是悬空指针
悬空指针指向已经释放或以其他方式不再有效的内存。
使用它会导致未定义行为:程序崩溃、数据损坏或难以察觉的错误。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof(int));
*p = 5;
printf("%d\n", *p);
free(p);
p = NULL;
return 0;
}释放后使用
最常见的原因是在free之后仍通过指针读取或写入数据。
这块内存可能已经被重新用于其他用途。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof(int));
*p = 42;
free(p);
/* Reading *p here would be use-after-free. */
p = NULL;
printf("avoided use-after-free\n");
return 0;
}释放后置空
释放后立即将指针设为NULL,可以让意外使用明确地导致崩溃,或者让free(NULL)安全地什么也不做。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *buf = malloc(10);
buf[0] = 'A';
free(buf);
buf = NULL;
if (buf == NULL) printf("buf is safely null\n");
return 0;
}返回局部地址
返回局部(栈)变量的地址会创建悬空指针:函数返回时,该变量就会消失。
请改用堆内存,或传入一个缓冲区。
#include <stdio.h>
#include <stdlib.h>
int *make_value(int v) {
int *p = malloc(sizeof(int));
*p = v;
return p;
}
int main(void) {
int *p = make_value(99);
printf("%d\n", *p);
free(p);
return 0;
}多个指针指向同一块内存
如果两个指针指向同一块内存,通过其中一个指针释放内存后,另一个指针就会变成悬空指针。
请跟踪所有权,确保只有所有者负责释放。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a = malloc(sizeof(int));
*a = 7;
int *b = a;
printf("%d\n", *b);
free(a);
a = NULL;
b = NULL;
printf("both nulled\n");
return 0;
}realloc 后悬空
当 realloc 移动一块内存时,任何指向原位置的旧指针都会变成悬空指针。
只能使用 realloc 返回的指针。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a = malloc(sizeof(int));
a[0] = 1;
int *b = realloc(a, 50 * sizeof(int));
/* a may now be dangling; use only b. */
b[0] = 2;
printf("%d\n", b[0]);
free(b);
return 0;
}在辅助函数中释放内存
如果辅助函数释放了一块内存,调用者持有的指针就会变成悬空指针。您可以在调用后将其置空,或者将辅助函数设计为接收指向指针的指针。
#include <stdio.h>
#include <stdlib.h>
void destroy(int **p) {
free(*p);
*p = NULL;
}
int main(void) {
int *p = malloc(sizeof(int));
*p = 5;
destroy(&p);
printf("p is %s\n", p == NULL ? "null" : "set");
return 0;
}为什么它很危险
释放后使用之所以危险,是因为这块内存现在可能存放着其他数据。写入操作可能破坏无关的程序状态,攻击者也可能利用这一点。
作用域导致的悬空指针
指向块作用域变量的指针,会在该代码块结束后失效。指针的持有时间不要超过其指向对象的生命周期。
#include <stdio.h>
int main(void) {
int *p;
{
int x = 10;
p = &x;
printf("in scope: %d\n", *p);
}
/* p now dangles; do not dereference it here. */
printf("block ended\n");
return 0;
}检测悬空指针
AddressSanitizer 和 Valgrind 等工具可以在运行时捕获释放后使用的问题,并指出确切的代码行。
开发期间请使用检测器进行构建。
代码中的安全生命周期回顾
分配、使用、释放,然后置空。始终遵循这一流程,可以避免指针变成悬空指针。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = malloc(sizeof(int));
if (!p) return 1;
*p = 123;
printf("%d\n", *p);
free(p);
p = NULL;
return 0;
}快速检查
请检验您对悬空指针的理解。
回顾
您已经了解了悬空指针:
- 它们指向已释放或超出作用域的内存;使用它们属于未定义行为。
- 避免返回局部变量的地址;在
free之后,或 realloc 移动内存后,绝不要继续使用原指针。 - 释放后将指针设为
NULL,并跟踪内存所有权。 - 使用检测器和 Valgrind 检测释放后使用的问题。
常见问题解答
「悬空指针」课时是免费的吗?
是的 — 「悬空指针」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C Academy 课程的其余内容,请升级到 CoddyKit PRO。 C Academy 课程共包含 4 节课。
「悬空指针」这节课中我会学到什么?
释放后使用的风险 你通过在浏览器中直接运行的动手代码来练习 C Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「悬空指针」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C Academy 课中编写并运行代码吗?
能。每节 C Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。