0Pricing
C Academy · 课时

悬空指针

释放后使用的风险

悬空指针 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. malloc 与 free
  2. calloc 与 realloc
  3. 内存泄漏
  4. 悬空指针
← 返回 C Academy