在 C++ 中利用堆栈与堆内存分配

Jinku Hu 2023年10月12日
在 C++ 中利用堆栈与堆内存分配

本文将解释如何在 C++ 中利用堆栈与堆内存分配的几种方法。

C++ 中堆栈与堆内存的区别

当我们想讨论内存的概念时,最好从运行最常见用户程序的系统的角度来考虑。大多数用户程序运行在操作系统环境中,它为我们管理硬件资源并处理用户程序无法处理的各种过于复杂或低效的任务。其中一项任务是直接管理硬件内存。因此,几乎所有的操作系​​统都提供了特殊的结构和功能来与硬件内存进行交互。操作系统提供的内存结构中的两个常见概念是堆栈和堆。

堆栈是为系统中每个正在运行的程序保留的内存区域,它以 LIFO 方式运行。即,当程序开始执行 main 函数时,后者获取其堆栈帧(堆栈内存的子集),其中自动存储局部变量和函数调用返回地址。一旦 main 调用另一个函数,就会在前一个堆栈帧之后以连续的方式创建一个新的堆栈帧。最新的堆栈帧将存储相应函数的本地对象,当它返回时,这些内存未被占用。

请注意,默认情况下,堆栈大小在大多数系统上是固定的,但如果用户有特殊需求,可以进行一定程度的自定义。堆栈内存的大小限制使其适用于小型且主要是临时对象。例如,Linux 操作系统中用户程序的默认堆栈大小为 8MB。它可能比程序可能需要处理的单个 JPEG 照片小,因此用户必须谨慎使用此空间。以下代码片段中声明的变量都存储在堆栈内存中。作为一般规则,如果每个局部变量没有像 staticvolatile 这样的特殊说明符,就会在堆栈上分配。

#include <iostream>

using std::cout;
using std::endl;

int main() {
  int var1;
  int var2 = 123;
  int arr1[4] = {1, 2, 3, 4};
  int var3 = var2;

  cout << var1 << endl;
  cout << var2 << endl;
  cout << var3 << endl;

  return EXIT_SUCCESS;
}

输出:

0
123
123

另一方面,有一个称为 - 堆(也称为空闲存储)的内存区域,可以在其中存储大对象并在运行时由程序员手动进行分配。这两个特性使堆内存本质上是动态的,因为它的大小不需要在编译时或程序执行期间的任何时刻确定。该程序可以调用特殊函数并从操作系统请求分配。请注意,从程序的角度来看,堆内存似乎是无限的,因为它不仅限于调用另一个分配函数来请求更多内存。虽然,操作系统管理所有正在运行的进程的内存;当没有更多可用的物理内存时,它可能会拒绝新的分配。

操作系统中的内存系统非常复杂,需要了解各种特定于操作系统/硬件的概念,因此我们在本主题中仅涵盖堆内存和堆栈内存的最低限度。C++ 语言中的堆内存手动管理可以使用 new/delete 操作符或 malloc/free 函数来完成。请注意,这些函数以类似的方式工作,用户通常指定要分配的字节数,并返回分配相同数量内存的地址。因此,程序员可以根据需要对给定的存储区域进行操作。

下一个代码示例演示了在堆内存上分配不同对象的几种情况。手动内存管理的一项重要功能是在不再需要分配的内存区域时将其返回给操作系统。后一个操作是使用对应于它们的分配对应物的 delete/free 调用完成的。如果程序不释放不需要的内存,则存在操作系统内存不足的风险,从而导致程序被终止。但是请注意,前面的问题主要发生在长时间运行的程序中,它们的特点是内存泄漏错误。

#include <iostream>

using std::cout;
using std::endl;

int main() {
  auto var4 = new int;
  cout << var4 << endl;

  int *arr2 = new int[4];
  auto arr3 = new int[4];
  cout << arr2 << endl;
  cout << arr3 << endl;

  delete var4;
  delete[] arr2;
  delete[] arr3;
  return EXIT_SUCCESS;
}
作者: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

DelftStack.com 创始人。Jinku 在机器人和汽车行业工作了8多年。他在自动测试、远程测试及从耐久性测试中创建报告时磨练了自己的编程技能。他拥有电气/电子工程背景,但他也扩展了自己的兴趣到嵌入式电子、嵌入式编程以及前端和后端编程。

LinkedIn Facebook

相关文章 - C++ Memory