15、堆内存vs栈内存

厨子大约 4 分钟C++C++基础编程程序厨

在编程中,对于内存的使用,无论是堆内存还是栈内存,都有它独特的应用场景和特点,大家可能很多编码设计的时候都在做选择,是使用堆内存还是栈内存呢?

本文将深入探讨堆内存和栈内存的特点,通过详细的代码示例,帮助大家理解什么场景下应该优先使用栈内存,什么场景下应该优先选择堆内存(文章观点相对主观,大家有不同看法欢迎留言讨论)。

我们首先需要明确什么是堆内存和栈内存。

什么是堆内存?

堆内存是用于存储动态分配的内存的区域。在C++中,我们可以通过newdelete来操作堆内存,在C中,我们可以通过 mallocfree来操作堆内存。堆内存的生命周期由开发者自己管理,如果忘记回收,可能会导致内存泄漏。我们也可以通过一些辅助工具来管理堆内存,比如智能指针,或其他RAII的封装工具。

什么是栈内存?

栈内存是用于存储局部变量、函数参数和返回地址等数据的区域。栈内存的分配和释放由编译器自动完成,不需要程序员手动管理。栈内存的生命周期由系统的调用和返回操作自动控制。

接下来,我们来看看堆空间和栈空间的特点。

堆空间和栈空间的特点

堆空间的主要特点

  • 动态分配,可以根据需要随时申请和释放。
  • 内存空间较大,但分配速度相对栈内存较慢。
  • 如果忘记释放,可能导致内存泄漏,我们常说的内存泄漏主要指的就是堆内存泄漏。

栈空间的主要特点

  • 自动分配和释放,无需程序员手动管理。
  • 内存空间较小,但分配速度快。
  • 由于栈空间有限,可能导致栈溢出,从而crash
  • 申请的栈空间一定是常量,不能是变量,无法动态分配。

如何避免栈溢出问题

虽然栈空间较小,容易导致栈溢出,但通过合理的编程规范,我们可以尽量避免这一问题。一般编码过程中都会避免在栈上分配大量数据,也避免过深的递归调用等。

我们通过一些代码示例来具体了解一下,详见注释。

堆内存示例:

int main() {
    int n = 10000;
    int *p = new int[n];  // 在堆上分配n个整数的空间,n可以是变量,可以动态分配。

    for (int i = 0; i < 10000; ++i) {
        p[i] = i;
    }

    delete[] p;  // 需要手动释放堆上的内存
    return 0;
}

栈内存示例:

int main() {
    int p[1000] = {0};  // 在栈上分配1000个整数的空间,编译时已确定栈空间大小,不能使用变量。
    for (int i = 0; i < 1000; ++i) {
        p[i] = i;

    }
    // 不需要手动释放内存
    return 0;

}

总结

在实际编程中,我们应该根据具体需求来选择使用堆内存或栈内存。

一般来说,对于那些生命周期长、大小不固定或者需要在多个函数间共享的数据,我们可以选择在堆上分配。

对于那些生命周期短、大小固定且只在单个函数内使用的数据,我们可以选择在栈上分配。

建议小对象优先使用栈内存,可以避免内存泄漏的问题,同时也能提高程序的运行效率。但是,当栈空间不足以满足需求时,我们就需要使用堆内存。

思考题

  • 如何要在多个线程间共享数据,使用堆内存还是栈内存?
  • 较大的A结构体(内部字段较多)如果要嵌套在B结构体中,使用堆内存还是栈内存?
  • 常见的STL,比如vectorstring,数据是在堆内存上还是栈内存上?