文章目录
前言
内存分配是C/C++编程中一个很重要的话题,它涉及到程序的性能、安全和灵活性。这篇文章给大家介绍一下C/C++如何进行内存分配。
一、C/C++内存分布
内存分配的五个区分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
- 堆是一个动态分配的内存空间,主要用于存放程序运行时创建的对象。堆的大小可以根据需要增长或缩小,但是需要手动管理分配和释放。在C语言中,我们使用malloc和free函数来操作堆内存;在C++中,我们使用new和delete运算符来操作堆内存。堆内存的优点是可以灵活地分配任意大小的内存空间,而且不受函数作用域的限制;缺点是容易造成内存泄漏、碎片和性能下降。
- 栈是一个自动分配的内存空间,主要用于存放函数调用时的临时变量,如局部变量、参数、返回值等。栈的大小是固定的,由系统决定,一般比堆小得多。栈的分配和释放是由编译器自动管理的,遵循先进后出的原则。当函数调用结束时,栈上的变量就会自动销毁。栈内存的优点是分配和释放速度快,而且不会产生内存泄漏;缺点是不能动态调整大小,而且受函数作用域的限制。
- 自由存储区是C++中一个特有的概念,它与堆是不同的。自由存储区是由new运算符分配的内存空间,它可以被delete运算符释放,也可以被C++标准库中的一些函数释放。自由存储区可以看作是堆的一个子集,它具有堆的特点,但也有一些额外的功能,如支持类类型对象的构造和析构。
- 全局/静态存储区是用于存放全局变量和静态变量的内存空间。这些变量在程序编译时就已经分配好,并在程序运行期间一直存在。全局/静态存储区可以分为两个部分:全局初始化区和全局未初始化区。全局初始化区是用于存放已经初始化的全局变量和静态变量;全局未初始化区是用于存放未初始化或者初始化为0的全局变量和静态变量1。
- 常量存储区是用于存放常量字符串和其他常量数据的内存空间。这些数据在程序编译时就已经确定,并在程序运行期间一直存在。常量数据不能被修改,否则会引发错误。
二、C语言动态内存管理方式
C语言提供了一些函数和运算符,使得程序员可以对内存进行操作,包括分配、释放、移动和复制等。这些函数和运算符主要有以下几种:
- malloc() 函数:用于动态分配内存。它接受一个参数,即需要分配的内存大小(以字节为单位),并返回一个指向分配内存的指针。如果分配失败,返回空指针。
- free() 函数:用于释放先前分配的内存。它接受一个指向要释放内存的指针作为参数,并将该内存标记为未使用状态。
- calloc() 函数:用于动态分配内存,并将其初始化为零。它接受两个参数,即需要分配的内存块数和每个内存块的大小(以字节为单位),并返回一个指向分配内存的指针。如果分配失败,返回空指针。
- realloc() 函数:用于重新分配内存。它接受两个参数,即一个先前分配的指针和一个新的内存大小,然后尝试重新调整先前分配的内存块的大小。如果调整成功,它将返回一个指向重新分配内存的指针,否则返回一个空指针。
注意: 动态分配的内存空间是在堆上分配的,而不是在栈上。
三、C++内存管理方式
1.new/delete操作内置类型
-
使用
new T
来分配一个类型为T的对象,并返回一个指向该对象的指针。例如,int* p = new int;
会在堆上分配一个int类型的对象,并把它的地址赋给p。int* p = new int(10);
动态申请一个int类型的空间并初始化为10。
使用delete p
来释放一个由new分配的对象,并调用其析构函数。例如,delete p;
会销毁p指向的int对象,并回收其占用的内存。 -
使用
new T[N]
来分配一个类型为T的数组,并返回一个指向该数组首元素的指针。例如,int* p = new int[10];
会在堆上分配一个包含10个int元素的数组,并把它的首地址赋给p。
使用delete[] p
来释放一个由new分配的数组,并调用其每个元素的析构函数。例如,delete[] p;
会销毁p指向的int数组,并回收其占用的内存。
2.new和delete操作自定义类型
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
// 动态申请一个A类型的空间并初始化为1。
A* p1 = new A(1);
delete p1;
A* p2 = new A[10];
delete[] p2;
}
同时注意配套使用,申请和释放单个元素的空间,使用new
和delete
操作符,申请和释放连续的空间,使用new[]
和delete[]
。
new/delete
和 malloc/free
最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数。
3.operator new与operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
- operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
- operator delete最终是通过free来释放空间的。
四、内存泄漏
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
所以我们今后在编写程序的过程中要注意通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。
总结
本文只是浅显的介绍了C/C++内存管理的一些方式,其底层更详细的原理大家感兴趣的话可以自行查阅相关资料。