一、内存区域划分
- 栈(Stack):存储局部变量、函数参数等,由编译器自动分配和释放,大小固定(通常几 MB),效率高。
- 堆(Heap) :动态分配的内存区域,大小灵活(可至 GB 级),需手动申请和释放,使用
new
/delete
或malloc
/free
操作。 - 全局 / 静态存储区:存储全局变量、静态变量,程序启动时分配,结束时释放。
- 常量存储区:存储字符串常量等,程序生命周期内存在。
- 代码区:存储程序的二进制指令,只读
二、动态内存管理方式
1. C 风格的内存管理(兼容 C++
-
malloc(size)
:在堆上分配size
字节的内存,返回void*
指针(需强制类型转换),分配失败返回NULL
。 -
free(ptr)
:释放malloc
分配的内存,ptr
必须是malloc
返回的指针,不可重复释放.cppint* 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;
三、常见问题与解决方法
-
内存泄漏
原因:动态分配的内存未被释放,导致内存耗尽。
解决:确保
new
与delete
、new[]
与delete[]
成对使用;优先使用智能指针。 -
野指针
原因:指针指向的内存已被释放,但指针未置空,后续误用该指针。
解决:释放内存后立即将指针设为
nullptr
。 -
重复释放
原因:对同一块内存多次调用
delete
或free
。解决:释放后将指针置空(
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
异常 (而非返回 NULL
或 nullptr
)。
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在申请空间后会调用构造函数完成空间中资源的清理释放.