前言:
内存管理
一、内存分布
内存分布通常可以分为以下几个区域:
-
栈(Stack):栈用于存储局部变量、函数参数和函数返回地址等信息。且向下增长
-
堆(Heap):堆用于动态分配内存,即通过
new
、malloc
等关键字在运行时分配内存。 -
数据段 (全局/静态存储区 Global/Static Storage Area):全局变量和静态变量在程序启动时被分配在全局/静态存储区域。全局变量在整个程序执行期间都存在,而静态变量具有局部作用域(局部静态变量,例如在函数中声明的静态变量)但生命周期与程序执行期间一样长。全局/静态存储区的内存分配在程序启动时完成,在程序结束时释放。
-
常量存储区(Constant Storage Area):常量字符串等常量数据存储在常量存储区,其内容在程序运行期间不可改变。常量存储区的内存通常位于程序的可执行文件中,因此称为常量存储区。
-
代码区(Code Area):**代码区存储程序的机器指令,即可执行代码。**代码区通常位于可执行文件的某个特定部分,在程序执行时被加载到内存中供CPU执行。
我们来看一下这些例子:
int globalVar = 1;//globalVar是全局变量,存在数据段上
static int staticGlobalVar = 1;//staticGlobalVar是静态变量,存在数据段上
int main(){
static int staticVar = 1;//staticVar局部静态变量,存在数据段上
int localVar = 1;//localVal是局部变量,在栈上
int num1[10] = { 1, 2, 3, 4 };//num1局部变量,在栈上
//char2是数组名,首元素地址,存在栈上;
//*char2,实际上是字符'a'且字符串"abcd"也是在栈中,所以*char2同样存在栈中
char char2[] = "abcd";
//pChar3是指针变量,存放在栈中;
//因为加了const修饰,所以字符串常量"abcd"存储在常量存储区,只读操作,不可修改;如果没有const修饰,就存在栈中,因此加了const修饰后,*pChar3储存在常量区
const char* pChar3 = "abcd";
//ptr1是指针变量,存在栈中;*ptr1是分配的内存,存在堆中
int* ptr1 = (int*)malloc(sizeof(int) * 4);
free(ptr1);
}
二、C和C++中的动态内存管理
C 中的动态内存管理
-
动态内存分配函数:C 中的动态内存分配函数包括
malloc()
,calloc()
,realloc()
。例如:int* ptr = (int*)malloc(sizeof(int) * 10); // 分配一个包含 10 个整数的内存块
-
释放动态分配的内存:使用
free()
函数可以释放动态分配的内存,防止内存泄漏。例如:free(ptr); // 释放动态分配的内存
C++ 中的动态内存管理
-
动态内存分配运算符:C++ 中使用
new
运算符来动态分配内存。例如:int* ptr1 = new int(1); // 分配一个整型并初始化 int* ptr2 = new int[10]; // 分配一个包含 10 个整数的内存 int* ptr3 = new int[5] = {1, 2, 3, 4, 5};// 分配五个整型并初始化
-
释放动态分配的内存:使用
delete
运算符释放动态分配的内存,与 C 中的free()
相对应。例如:// 释放动态分配的内存 delete ptr1; delete[] ptr2; delete[] ptr3;
三、operator new与operator delete函数
new
和delete
是用户进行动态内存申请和释放的操作符,operator new
和operator delete
是系统提供的全局函数,new
在底层调用operator new
全局函数来申请空间,delete
在底层通过operator delete
全局函数来释放空间。
而operator new
和 operator delete
在大多数系统中,底层会使用 malloc
和 free
来分配或释放内存,但在一些特定的环境中,可能会使用其他的内存分配和释放函数。
四、定位 new (了解)
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
一般的 new
表达式会分配内存并在该内存上构造对象,而定位 new
表达式则允许你提供一个已经分配的内存地址来构造对象。
定位new表达式的基本语法:
new (pointer) Type(initializer)
其中,pointer
是一个指向要构造对象的内存位置的指针,Type
是要构造的对象类型,initializer
是可选的初始化参数。
以下是一个示例,演示了如何使用定位new表达式:
#include <iostream>
class MyClass {
public:
MyClass(int value) : m_value(value) {
std::cout << "Constructing MyClass with value: " << m_value << std::endl;
}
private:
int m_value;
};
int main() {
// 分配内存
char buffer[sizeof(MyClass)];
// 在给定内存位置上构造对象
MyClass* obj = new (buffer) MyClass(42);
return 0;
}
在这个示例中,我们首先分配了足够大的内存缓冲区(buffer
),然后使用定位new表达式在该缓冲区上构造了一个 MyClass
对象。注意,我们在构造对象后,可以像常规指针一样使用该对象。
定位new表达式的一个常见用途是在特定的内存位置上构造对象,比如在实现自定义内存池或者对象池时。
五、malloc/free和new/delete的区别
不同点:
-
malloc
和free
是 C 语言中的函数,而new
和delete
是 C++ 中的操作符。 -
malloc
分配的内存不会初始化,而new
可以初始化。但是需要注意,对于内置类型(如int
、double
等),new
分配的内存并不会被初始化。 -
malloc
需要手动计算空间大小并传递,而new
不需要,因为它知道要分配的类型的大小。对于数组,new
可以使用[]
指定对象个数。 -
malloc
的返回值是void*
,需要显式转换为目标类型,而new
返回的是所分配类型的指针,不需要显式转换。 -
当内存不足时,
malloc
返回NULL
,需要检查是否为NULL
,而new
抛出std::bad_alloc
异常。 -
对于自定义类型,
malloc
和free
只是分配和释放内存,并不会调用构造函数和析构函数。而new
在分配内存后会调用构造函数初始化对象,delete
在释放内存前会调用析构函数。
共同点:
- 都用于从堆上申请空间,并且需要用户手动释放。
如果你喜欢这篇文章,点赞👍+评论+关注⭐️哦!
欢迎大家提出疑问,以及不同的见解。