内存管理(C/C++)

一、内存区域划分

  1. 栈(Stack):存储局部变量、函数参数等,由编译器自动分配和释放,大小固定(通常几 MB),效率高。
  2. 堆(Heap) :动态分配的内存区域,大小灵活(可至 GB 级),需手动申请和释放,使用 new/deletemalloc/free 操作。
  3. 全局 / 静态存储区:存储全局变量、静态变量,程序启动时分配,结束时释放。
  4. 常量存储区:存储字符串常量等,程序生命周期内存在。
  5. 代码区:存储程序的二进制指令,只读

二、动态内存管理方式

1. C 风格的内存管理(兼容 C++
  • malloc(size):在堆上分配 size 字节的内存,返回 void* 指针(需强制类型转换),分配失败返回 NULL

  • free(ptr):释放 malloc 分配的内存,ptr 必须是 malloc 返回的指针,不可重复释放.

    cpp 复制代码
    int* arr = (int*)malloc(10 * sizeof(int)); // 分配10个int的内存
    if (arr != NULL) {
        // 使用内存
        free(arr); // 释放内存
        arr = NULL; // 避免野指针
    }
2. C++ 风格的内存管理(推荐)

new/delete:用于单个对象的动态管理

  • new 会先分配内存,再调用对象的构造函数。

  • delete 会先调用对象的析构函数,再释放内存

    cpp 复制代码
    // 动态创建对象数组
    MyClass* arr = new MyClass[5]; // 5个MyClass对象
    delete[] arr; // 释放数组,注意必须用delete[]
    arr = nullptr;

    三、常见问题与解决方法

  • 内存泄漏

    原因:动态分配的内存未被释放,导致内存耗尽。

    解决:确保 newdeletenew[]delete[] 成对使用;优先使用智能指针。

  • 野指针

    原因:指针指向的内存已被释放,但指针未置空,后续误用该指针。

    解决:释放内存后立即将指针设为 nullptr

  • 重复释放

    原因:对同一块内存多次调用 deletefree

    解决:释放后将指针置空(delete nullptr 是安全的)。

  • 内存越界

    原因:访问了超出分配范围的内存(如数组越界)。

    解决:严格检查访问范围,使用标准库容器(如 std::vector)替代原生数组。

1.malloc/calloc/relloc的区别

mallo分配的内存未初始化,内容是随机的垃圾值

calloc分配的内存会被初始化为0

realloc调整已分配内存块的大小

2.new和delete

cpp 复制代码
new和delete
int main()
{
	int* p1 = new int;
	int* p2 = new int[10];



	delete p1;
	delete[]p2;

	//申请对象+初始化
	int* p3 = new int(0);
	int* p4 = new int[10]{0};
	int* p5 = new int[10]{1,2,3,4,5};

	delete p3;
	delete []p4;
	delete []p5;

	return 0;
}

new 不仅分配内存,还会自动调用对象的构造函数(完成对象初始化);

delete 不仅释放内存,还会自动调用对象的析构函数(完成资源清理)。]

new失败

new 无法分配所需内存时,会抛出 std::bad_alloc 异常 (而非返回 NULLnullptr)。

cpp 复制代码
new失败
#include <iostream>
#include <new> // 包含 bad_alloc 定义

int main() {
    try {
        // 尝试分配大量内存(可能失败)
        int* p = new int[1000000000000]; // 假设内存不足
        // 分配成功时的操作
        delete[] p;
    }
    catch (const std::bad_alloc& e) {
        // 捕获分配失败的异常
        std::cout << "内存分配失败:" << e.what() << std::endl;
    }
    return 0;
}

new和delete的实现原理

new的原理,new的底层

1.调用 operator new函数申请空间

2.在申请的空间上执行构造函数,完成对象的构造(初始化)

实际上还是malloc+构造

delete

1.实际上还是free()+析构

2.内置类型没有构造函数,析构函数的概念.

3.自定义类型有相应的概念,会调用构造函数和析构函数

cpp 复制代码
#include <iostream>
#include <cstdlib>  // 包含 malloc/free
class MyClass {
public:
    MyClass() {
        std::cout << "调用构造函数\n";
    }
    ~MyClass() {
        std::cout << "调用析构函数\n";
    }
};
int main() {
    // 自定义类型使用 new/delete
    MyClass* p1 = new MyClass;  // 1. operator new 分配内存  2. 调用构造函数
    delete p1;                  // 1. 调用析构函数          2. operator delete 释放内存

    // 内置置类型使用 new/delete(无构造/析构)
    int* p2 = new int;          // 仅分配内存(类似 malloc)  malloc(sizeof(int))
    delete p2;                // 仅释放内存(类似 free)    free(p2);
    return 0;
}

在 C++ 中使用 new 分配多个对象(如数组)时,对于自定义类型,还会额外处理构造 / 析构相关的逻辑。

内置类型不会

自定义类型:

分配的内存大小 = 单个对象大小 × 元素数量 + 可能的额外空间(用于存储数组长度,以便 delete[] 时正确调用析构函数)

malloc/free和new/delete的区别

共同点:

malloc/free和new/delete的共同点:都是从堆上申请空间,并且需要用户手动释放.

不同点:

1.malloc和free是函数,new和delete是操作符

2.malloc申请空间不会初始化,new可以初始化.

3.malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间类型即可,如果是多个对象,[]中指定对象个数即可.

4.malloc的返回值为void*,在使用时必须强转,new不需要,因为new后面跟的是空间的类型.

5.malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常.

6.申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成空间中资源的清理释放.

相关推荐
superlls2 小时前
(设计模式)区分建造者、 规格模式(MyBatis Example+Criteria )
java·tomcat
饭碗的彼岸one2 小时前
C++设计模式之单例模式
c语言·开发语言·c++·单例模式·设计模式·饿汉模式·懒汉模式
kimble_xia@oracle2 小时前
SQL 笔记
java·数据库·oracle
David爱编程2 小时前
深度解析:synchronized 性能演进史,从 JDK1.6 到 JDK17
java·后端
Tim_102 小时前
【算法专题训练】20、LRU 缓存
c++·算法·缓存
Vect__3 小时前
从零实现一个简化版string 类 —— 深入理解std::string的底层设计
c++
脑子慢且灵3 小时前
【JavaWeb】一个简单的Web浏览服务程序
java·前端·后端·servlet·tomcat·web·javaee
hope_wisdom3 小时前
C/C++数据结构之栈基础
c语言·数据结构·c++··stack
青铜发条3 小时前
【Qt】PyQt、原生QT、PySide6三者的多方面比较
开发语言·qt·pyqt