本文是C++的内存模型,动态内存和智能指针相关总结,适合复习和面试!
C++的基本内存模型
在大多数现代操作系统和硬件架构中,程序的内存布局从高地址到低地址的排序通常如下:
栈(Stack):
栈位于内存的高地址部分,它向下增长,即向低地址方向扩展。栈用于存储局部变量、函数参数、返回地址等。它是一种具有后进先出(LIFO)特性的数据结构。每当一个函数被调用时,一个新的栈帧(Stack Frame)就会被创建,用于存储该函数的上下文信息。当函数执行完毕返回时,它的栈帧就会被销毁。
堆(Heap):
堆紧随栈之下,位于中间的内存区域。堆向上增长,即向高地址方向扩展。堆用于动态内存分配。程序在运行时可以通过如new、delete、malloc、free等函数来分配和释放内存。堆的大小不是固定的,可以根据程序的需要动态扩展或收缩。堆的内存管理是程序员的责任,不当的管理可能导致内存泄漏等问题。
BSS段(Block Started by Symbol):
BSS段用于存储程序中未初始化的全局变量和静态变量。在程序启动时,操作系统会将这部分内存初始化为零。BSS段的名字来源于早期汇编语言中的一个伪指令,表示从某个符号开始的一块内存区域。
数据段(Data Segment):
数据段又称为初始化数据段,用于存储程序中初始化的全局变量和静态变量。这些数据在程序开始执行前由编译器和链接器分配,并在程序加载到内存时初始化。
代码段(Code Segment or Text Segment):
代码段位于内存的低地址部分,包含程序的机器指令。代码段存储程序的可执行指令,也就是编译后的机器码。这部分内存通常是只读的,以防止程序代码被意外修改。代码段有时也被称为文本段。
高地址
+------------------+
| 栈(Stack) |
+------------------+
| 堆(Heap) |
+------------------+
| BSS段(未初始化数据)|
+------------------+
| 数据段(初始化数据)|
+------------------+
| 代码段(程序代码) |
+------------------+
低地址
动态内存与智能指针
C++编程中,动态内存分配是一个重要的概念,它允许程序在运行时分配内存。这是通过使用new
和delete
操作符来完成的,它们分别用于分配和释放内存。然而,手动管理动态分配的内存可能会导致错误,如内存泄漏(忘记释放内存)和悬垂指针(指向已释放内存的指针)。为了解决这些问题,C++引入了智能指针的概念。
智能指针是模板类,它们提供了类似于指针的接口,但是增加了自动内存管理的功能。使用智能指针,当智能指针超出作用域或被删除时,它们所指向的内存会自动被释放。C++标准库提供了几种不同类型的智能指针,每种都有其特定的用途:
std::unique_ptr
: 它提供了对动态分配对象的独占所有权。这意味着std::unique_ptr
不能被复制,只能被移动。当std::unique_ptr
被销毁时,它所指向的对象也会被自动删除。它通常用于确保资源只被一个所有者拥有。
std::unique_ptr<int> ptr(new int(10));
// 使用 *ptr 访问值
std::shared_ptr
: 它允许多个智能指针共享对同一个对象的所有权。std::shared_ptr
使用引用计数来追踪有多少个智能指针指向同一个对象。当最后一个std::shared_ptr
被销毁时,对象会被自动删除。
std::shared_ptr<int> ptr1(new int(10));
std::shared_ptr<int> ptr2 = ptr1; // ptr1 和 ptr2 共享所有权
std::weak_ptr
: 它设计用来解决std::shared_ptr
的循环引用问题。std::weak_ptr
不会增加引用计数,因此它不会阻止所指向的对象被销毁。它通常用于跟踪对象,但不需要拥有对象。
std::shared_ptr<int> ptr1(new int(10));
std::weak_ptr<int> ptr2 = ptr1; // ptr2 不会增加 ptr1 的引用计数