深入理解 C/C++ 内存管理:从内存布局到动态分配

内存管理是 C/C++ 程序设计中至关重要的一环,它不仅关系到程序的性能,还直接影响程序的稳定性和安全性。本文将系统性地介绍 C/C++ 中的内存分布、动态内存管理方式及其底层原理,帮助你构建清晰的内存管理知识体系。

目录

[一、C/C++ 程序内存布局](#一、C/C++ 程序内存布局)

示例分析

[二、C 语言动态内存管理:malloc / calloc / realloc / free](#二、C 语言动态内存管理:malloc / calloc / realloc / free)

常见面试题

[三、C++ 内存管理方式:new / delete](#三、C++ 内存管理方式:new / delete)

基本使用

[与 malloc/free 的最大区别](#与 malloc/free 的最大区别)

[四、operator new 与 operator delete](#四、operator new 与 operator delete)

[自定义 operator new/delete](#自定义 operator new/delete)

[五、new 和 delete 的实现原理](#五、new 和 delete 的实现原理)

[5.1 内置类型](#5.1 内置类型)

[5.2 自定义类型](#5.2 自定义类型)

[六、定位 new 表达式(placement new)](#六、定位 new 表达式(placement new))

[七、malloc/free 与 new/delete 的区别总结](#七、malloc/free 与 new/delete 的区别总结)

结语



一、C/C++ 程序内存布局

在 C/C++ 程序中,内存通常被划分为以下几个区域:

区域 存储内容
栈(Stack) 局部变量、函数参数、返回值等,由编译器自动分配和释放,向下增长
堆(Heap) 动态分配的内存,由程序员手动管理(malloc/free、new/delete),向上增长
数据段(Data Segment) 全局变量、静态变量(static)。
代码段(Code Segment) 可执行代码、字符串常量等只读数据。
内存映射段(Memory Mapping Segment) 文件映射、动态库、匿名映射等。

示例分析

cpp

复制代码
int globalVar = 1;               // 数据段
static int staticGlobalVar = 1;  // 数据段

void Test() {
    static int staticVar = 1;    // 数据段
    int localVar = 1;            // 栈
    int num1[10] = {1, 2, 3, 4}; // 栈
    char char2[] = "abcd";       // 栈(数组在栈上,字符串内容在栈中复制)
    const char* pchar3 = "abcd"; // pchar3在栈,"abcd"在代码段
    int* ptr1 = (int*)malloc(sizeof(int) * 4); // ptr1在栈,指向堆内存
}

二、C 语言动态内存管理:malloc / calloc / realloc / free

C 语言提供了以下动态内存分配函数:

函数 说明
malloc 分配指定字节数的未初始化内存。
calloc 分配并清零内存,适用于数组。
realloc 调整已分配内存的大小,可能移动内存块。
free 释放已分配的内存。

常见面试题

  1. malloc / calloc / realloc 的区别?

    • malloc 只分配不初始化;

    • calloc 分配并初始化为 0;

    • realloc 用于调整内存大小。

  2. malloc 的实现原理?

    • 在 glibc 中,malloc 通过维护一个内存块链表来管理堆内存,使用首次适应、最佳适应等算法分配内存。

三、C++ 内存管理方式:new / delete

C++ 引入了 newdelete 操作符,它们在 C 语言的基础上增加了对对象生命周期的管理。

基本使用

cpp

复制代码
// 单个对象
int* p1 = new int;
int* p2 = new int(10);  // 初始化为 10
delete p1;
delete p2;

// 对象数组
int* p3 = new int[10];
delete[] p3;

与 malloc/free 的最大区别

对于自定义类型new 会调用构造函数,delete 会调用析构函数,而 malloc/free 不会。

cpp

复制代码
class A {
public:
    A() { cout << "构造" << endl; }
    ~A() { cout << "析构" << endl; }
};

A* p1 = (A*)malloc(sizeof(A));  // 不会调用构造函数
A* p2 = new A;                   // 调用构造函数
free(p1);                        // 不会调用析构函数
delete p2;                       // 调用析构函数

四、operator new 与 operator delete

newdelete 在底层调用的是全局函数 operator newoperator delete

  • operator new 内部调用 malloc,失败时抛出 std::bad_alloc 异常;

  • operator delete 内部调用 free

自定义 operator new/delete

我们可以重载这两个函数来实现自定义内存管理策略,例如内存池。


五、new 和 delete 的实现原理

5.1 内置类型

malloc/free 类似,区别在于:

  • new 失败时抛异常,malloc 返回 NULL

  • new[]/delete[] 用于连续空间。

5.2 自定义类型

  • new

    1. 调用 operator new 分配内存;

    2. 调用构造函数初始化对象。

  • delete

    1. 调用析构函数清理资源;

    2. 调用 operator delete 释放内存。


六、定位 new 表达式(placement new)

定位 new 用于在已分配的内存上构造对象,常用于内存池场景。

cpp

复制代码
A* p = (A*)malloc(sizeof(A));
new(p) A();        // 在 p 指向的内存上调用构造函数
p->~A();           // 显式调用析构函数
free(p);

七、malloc/free 与 new/delete 的区别总结

特性 malloc/free new/delete
语言 C 函数 C++ 操作符
初始化 不初始化 可初始化
大小计算 手动计算 自动计算(类型)
返回值 void*(需强转) 类型指针(无需强转)
失败处理 返回 NULL 抛出异常
对象生命周期管理 不调用构造/析构函数 调用构造/析构函数

结语

理解 C/C++ 内存管理是写出高效、稳定程序的基础。从内存布局到动态分配,从 malloc/freenew/delete,再到底层的 operator new/delete 和定位 new,每一个环节都值得深入学习和实践。希望本文能帮助你构建清晰的内存管理知识框架。


本文基于《C/C++ 内存管理》学习笔记整理而成,适用于面试准备和系统学习。建议结合实际代码练习,加深理解。

相关推荐
克喵的水银蛇2 小时前
Flutter 通用标签选择组件:TagSelector 支持单选 / 多选
javascript·windows·flutter
JANGHIGH2 小时前
c++ 多线程(三)
开发语言·c++
txzz88882 小时前
网络应用netstart命令
网络·windows·计算机网络·microsoft
长安er2 小时前
LeetCode 34排序数组中查找元素的第一个和最后一个位置-二分查找
数据结构·算法·leetcode·二分查找·力扣
点云SLAM2 小时前
C++ 中traits 类模板(type traits / customization traits)设计技术深度详解
c++·算法·c++模板·c++高级应用·traits 类模板·c++17、20·c++元信息
CoderYanger2 小时前
动态规划算法-两个数组的dp(含字符串数组):48.最长重复子数组
java·算法·leetcode·动态规划·1024程序员节
liu****3 小时前
9.二叉树(一)
c语言·开发语言·数据结构·算法·链表
sin_hielo3 小时前
leetcode 3577
数据结构·算法·leetcode
ACERT3333 小时前
04矩阵理论复习-矩阵的分解
算法·矩阵