Logo成贤计协指南

有关指针的常见问题

在学习 C/C++ 的过程中,指针是一个非常重要但又容易“踩坑”的知识点。很多初学者~~(老手也一样)~~在使用指针时,经常会遇到各种各样的问题,比如野指针、悬空指针、内存泄漏、内存溢出等。这些问题不仅可能导致程序崩溃,还可能造成数据丢失,甚至带来安全隐患。

本节将带你认识指针使用中最常见的几个“坑”,并教你如何避免它们。

野指针

野指针指的是指向了一个“未知”或“不可预知”内存地址的指针。简单来说,就是指针没有被正确初始化,或者被错误地赋值,导致它指向了一个不属于你的内存区域。

举个例子:

int* p; // 只是声明了一个指针,但没有初始化
*p = 10; // ❌ 错误!会导致程序崩溃或数据异常

上面的代码中,p 没有被赋初值,它的值是随机的(内存垃圾),直接使用会让程序访问到未知的内存区域,可能会直接崩溃,或者出现莫名其妙的错误。

如何避免野指针?

  • 指针定义时要初始化,比如设为 nullptr(C++11及以后)或 NULL(C/C++98):

    int* p = nullptr; // 推荐
  • 只有在指针被正确赋值后(比如指向一个有效变量或动态分配的内存),再使用它。

悬空指针

悬空指针指的是指向了已经被释放的内存区域的指针。比如使用 deletefree 释放了内存,但指针还在使用,就会导致悬空指针。

举个例子:

int* p = new int(10); // 在堆上分配了一个 int
delete p;             // 释放了这块内存
*p = 20;              // ❌ 错误!p 已经变成悬空指针

在上面的代码中,delete p 之后,p 仍然保存着原来的地址,但这块内存已经被系统收回了。继续使用 *p 就会访问到无效的内存,导致程序崩溃或数据异常。

如何避免悬空指针?

  • 当你用 deletefree 释放了指针指向的内存后,最好马上把指针设为 nullptr,避免误用:

    delete p;
    p = nullptr; // 防止悬空指针
  • 不要返回函数内部局部变量的指针或引用,因为函数结束后这些变量会被销毁。

当然,最重要的还是在使用指针前仔细阅读上下文,确保指针没有在上文的某地已经被释放。

内存泄漏

内存泄漏是指程序运行过程中,分配的内存没有被释放,导致这部分内存无法再被使用,最终程序占用的内存越来越多,甚至可能导致系统卡死或崩溃。

举个例子:

void func() {
    int* p = new int(10);
    // 没有 delete p
} // p 指向的内存没有释放,造成内存泄漏

如果你多次调用 func(),每次都会分配新的内存,但 p 从来没有释放,只是一味的开辟新的空间,最终会导致内存泄漏。

如何避免内存泄漏?

  • 动态分配的内存用完后一定要记得释放:

    int* p = new int(10);
    // ...使用 p
    delete p; // 用完后释放

    如果是 C 语言,动态分配的内存用 malloc,用完后要记得 free

  • 在现代 C++ 中,推荐使用智能指针(如 std::unique_ptrstd::shared_ptr),它们会自动管理内存,减少内存泄漏的风险:

    #include <memory>
    std::unique_ptr<int> p(new int(10));
    // 不需要手动 delete,离开作用域会自动释放

内存溢出

内存溢出是指程序试图访问超出分配范围的内存空间,或者分配了过多的内存,导致系统无法满足请求。常见的情况有数组越界、无限递归、分配超大内存等。

举个例子:

int arr[5];
arr[10] = 100; // ❌ 错误!数组越界,访问了未分配的内存

或者:

while (true) {
    int* p = new int[1000000]; // 不断分配大块内存,最终会导致系统崩溃
}

如何避免内存溢出?

  • 使用数组时要注意下标范围,不能越界。
  • 动态分配内存时要合理控制分配的大小,避免分配过多。
  • 递归时要设置好终止条件,避免无限递归导致栈溢出。

小结

指针是 C/C++ 的强大工具,但也是“危险品”。只要你养成良好的习惯,注意初始化和释放,就能大大减少指针相关的错误。当然,在现代编程语言中,我们会遇到很多有关内存安全的方法,比如智能指针(C++ 中的 unique_ptrshared_ptr)、垃圾回收(在 Java、Go 以及大多数脚本语言中存在)、所有权系统(存在于 Rust 为首的部分语言中)等等,都是为了帮助我们更安全地管理内存。在工程实践中,我们更加推荐使用这些现代工具,尽量避免手动管理内存,从而减少指针相关的问题。