在这里插入图片描述

前言:
内存管理


一、内存分布

内存分布通常可以分为以下几个区域:

  1. 栈(Stack):栈用于存储局部变量、函数参数和函数返回地址等信息。且向下增长

  2. 堆(Heap):堆用于动态分配内存,即通过 newmalloc 等关键字在运行时分配内存。

  3. 数据段 (全局/静态存储区 Global/Static Storage Area)全局变量和静态变量在程序启动时被分配在全局/静态存储区域。全局变量在整个程序执行期间都存在,而静态变量具有局部作用域(局部静态变量,例如在函数中声明的静态变量)但生命周期与程序执行期间一样长。全局/静态存储区的内存分配在程序启动时完成,在程序结束时释放。

  4. 常量存储区(Constant Storage Area)常量字符串等常量数据存储在常量存储区,其内容在程序运行期间不可改变。常量存储区的内存通常位于程序的可执行文件中,因此称为常量存储区。

  5. 代码区(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 中的动态内存管理

  1. 动态内存分配函数:C 中的动态内存分配函数包括 malloc(), calloc(), realloc()。例如:

    int* ptr = (int*)malloc(sizeof(int) * 10); // 分配一个包含 10 个整数的内存块
    
  2. 释放动态分配的内存:使用 free() 函数可以释放动态分配的内存,防止内存泄漏。例如:

    free(ptr); // 释放动态分配的内存
    

C++ 中的动态内存管理

  1. 动态内存分配运算符:C++ 中使用 new 运算符来动态分配内存。例如:

    int* ptr1 = new int(1); // 分配一个整型并初始化
    int* ptr2 = new int[10]; // 分配一个包含 10 个整数的内存
    int* ptr3 = new int[5] = {1, 2, 3, 4, 5};// 分配五个整型并初始化
    
  2. 释放动态分配的内存:使用 delete 运算符释放动态分配的内存,与 C 中的 free() 相对应。例如:

    // 释放动态分配的内存
    delete ptr1;
    delete[] ptr2; 
    delete[] ptr3;
    

三、operator new与operator delete函数

newdelete是用户进行动态内存申请和释放的操作符,operator newoperator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

operator new operator delete 在大多数系统中,底层会使用 mallocfree来分配或释放内存,但在一些特定的环境中,可能会使用其他的内存分配和释放函数。

在这里插入图片描述

四、定位 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的区别

不同点:

  1. mallocfree 是 C 语言中的函数,而 newdelete 是 C++ 中的操作符。

  2. malloc 分配的内存不会初始化,而 new 可以初始化。但是需要注意,对于内置类型(如 intdouble 等),new 分配的内存并不会被初始化。

  3. malloc 需要手动计算空间大小并传递,而 new 不需要,因为它知道要分配的类型的大小。对于数组,new 可以使用 [] 指定对象个数。

  4. malloc 的返回值是 void*,需要显式转换为目标类型,而 new 返回的是所分配类型的指针,不需要显式转换。

  5. 当内存不足时,malloc 返回 NULL,需要检查是否为 NULL,而 new 抛出 std::bad_alloc 异常。

  6. 对于自定义类型,mallocfree 只是分配和释放内存,并不会调用构造函数和析构函数。而 new 在分配内存后会调用构造函数初始化对象,delete 在释放内存前会调用析构函数。

共同点:

  • 都用于从堆上申请空间,并且需要用户手动释放。

在这里插入图片描述
如果你喜欢这篇文章,点赞👍+评论+关注⭐️哦!
欢迎大家提出疑问,以及不同的见解。

Logo

欢迎加入西安开发者社区!我们致力于为西安地区的开发者提供学习、合作和成长的机会。参与我们的活动,与专家分享最新技术趋势,解决挑战,探索创新。加入我们,共同打造技术社区!

更多推荐