【C/C++】动态内存管理(C:malloc,realloc,calloc,free || C++:new,delete)

2024-06-04 3372阅读

【C/C++】动态内存管理(C:malloc,realloc,calloc,free || C++:new,delete) 第1张

🔥个人主页: Forcible Bug Maker

🔥专栏: C++ | | C语言

目录

  • 前言
  • C/C++内存分布
  • C语言中的动态内存管理:malloc/realloc/realloc/free
    • `malloc`
    • `realloc`
    • `calloc`
    • `free`
    • C++中的动态内存管理:new/delete
      • new和delete操作内置类型
      • new和delete操作自定义类型
      • operator new与operator delete函数
      • new和delete的实现原理
      • 定位new表达式(placement-new)
      • 结语

        前言

        本篇博客主要内容:C++和C语言的动态内存管理方式,机制以及两者之间的区别。

        在学习C语言的过程中,也曾涉及过动态内存管理,我们可以使用malloc,realloc,calloc等函数来动态管理堆中空间资源。而在C++中,有了新的动态内存管理方式,那就是new和delete关键字。忽然发现之前似乎并没有讲C语言的几个动态内存管理函数,所以标题是 【C/C++】动态内存管理 ,不过不止如此,本次还会介绍new,delete关键字的底层,并区分一下C和C++内存管理之间的不同。

        C/C++内存分布

        在开始讲解之前,想通过一道题引入今天的内容。

        #include
        using namespace std;
        int globalVar = 1;
        static int staticGlobalVar = 1;
        void Test()
        {
        	static int staticVar = 1;
        	int localVar = 1;
        	int num1[10] = { 1, 2, 3, 4 };
        	char char2[] = "abcd";
        	const char* pChar3 = "abcd";
        	int* ptr1 = (int*)malloc(sizeof(int) * 4);
        	int* ptr2 = (int*)calloc(4, sizeof(int));
        	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
        	free(ptr1);
        	free(ptr3);
        }
        

        数据段就是我们常说的静态区,代码段就是我们所说的常量区。而动态内存管理,主要管理的是堆区中的内存空间。

        选择题:

        选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

        ①g/lobalVar在哪里?②____ staticGlobalVar在哪里?___

        ③//staticVar在哪里?___ ④localVar在哪里?____

        ⑤num1 在哪里?____

        1. globalVal是全局变量,存储在数据段(静态区)。
        2. staticGlobalVal是静态全局变量,同时是静态的,因此它同样存储在数据段(静态区)。
        3. staticVar是静态局部变量,存储在数据段(静态区),其生命周期贯穿整个程序的执行。
        4. localVar是局部变量,存储在栈上。
        5. num1是局部变量,本质是数组指针(存储着数组首元素地址)存储在栈上。

        选择题:

        ①char2在哪里?____ ②char2在哪里?____

        ③pChar3在哪里?___ _④pChar3在哪里?_____

        ⑤ptr1在哪里?____ ⑥*ptr1在哪里?____

        1. char2和num1性质是一样的,为局部变量,是数组首元素地址,存储在栈上。
        2. *char2是栈帧中直接开辟空间存储的数据,在栈上。
        3. pChar3是局部指针变量,存储在栈上。
        4. *pChar3指向的内容(为常量字符串"abcd")存储在代码段(常量区)。
        5. ptr1是局部指针变量,存储在栈上。
        6. *ptr1指向的内存空间是通过malloc动态开辟的,存储在堆上。
        • *char2(局部字符数组)

          当声明一个局部字符数组并用一个字符串字面量初始化的时候,如char char2[] = "abcd";或char char2[5] = "abcd";时,编译器在栈上会为数组分配内存,然后将字符串字面量的内容(包括结尾的**\0**)复制到这块内存中。因此char2指向的内容存储在栈上。

        • *pChar(字符串字面量指针)

          当你使用指针去指向一个字符串常量(“abcd”)时,由于字符串常量是存储在代码段(常量区) 的,所以必须用const修饰才能接收到常量内容的地址(这样规定的原因是为了防止你对常/量做出修改,此时的"abcd"就跟数字1,2,3等等常量的性质是一样的)。故const char* pChar3 = "abcd";中,尽管pChar是一个指针,存储在栈中,但其却指向的字符串却存储在常量区。

          *char2不在常量区,因为char2是局部字符数组,其内容直接存储在栈上。

          *pChar3在常量区,是因为它指向的是一个字符串字面量(“abcd”),字符串字面量存储在程序的常量区,这部分内存只能读,不能改动。

          【C/C++】动态内存管理(C:malloc,realloc,calloc,free || C++:new,delete) 第2张

          内存主要可以分为几个部分:栈(Stack)、堆(Heap)、数据段(Data Segment)和代码段(Code Segment)。而我们的数据通常也存储在这些地方。

          1. 栈(Stack):

            自动变量(包括局部变量和函数参数)通常分配在栈上。当函数被调用时,它的参数和局部变量会在栈上分配空间。当函数返回时,这些空间会被自动释放。

            栈内存分配和释放由编译器自动处理,不需要程序员手动管理。

          2. 堆(Heap):

            通过malloc、calloc、realloc等函数分配的内存位于堆上。堆用于动态内存分配,程序员需要手动管理内存的分配和释放。

            堆上的内存可以在程序的运行期间随时分配和释放,适用于需要动态创建和销毁的对象。

          3. 静态存储区:

            全局变量和静态变量(无论是在函数内部还是外部声明的静态变量)存储在全局/静态存储区。

            这些变量在程序的整个生命周期中都存在,不会被自动释放。它们的初始化发生在程序启动时,释放则发生在程序结束时。

          4. 常量区:

            存储常量字符串和字面量的地方。这些常量在程序的生命周期内都是固定的,不会被修改。

          C语言中的动态内存管理:malloc/realloc/realloc/free

          C语言里,关于动态内存的管理,是依靠一套标准的库函数完成的,它们包括malloc,realloc,calloc和free。这些函数允许在程序中随时开辟,分配和调整堆中的内存,都统一放在头文件中。下面是关于这些函数的基本用法以及它们之间的区别:

          malloc

          • 头文件:
          • 函数声明: void* malloc(size_t size);
          • 功能: 在堆中开辟指定字节数的未初始化内存,并返回一个指向新开辟内存的指针。如果分配失败,则返回空指针NULL。
          • 示例:
            // 为代码开辟了大小能存放四个整型数据的空间
            int* ptr = (int*)malloc(sizeof(int) * 4);
            // 这个ptr指针,指向内存空间首元素地址
            *ptr = 1;
            *(ptr + 1) = 2;
            *(ptr + 2) = 3;
            *(ptr + 3) = 4;
            for (int i = 0; i  
            

            【C/C++】动态内存管理(C:malloc,realloc,calloc,free || C++:new,delete) 第3张

            realloc

            • 头文件:
            • 函数声明: void* realloc(void* ptr,size_t size);
            • 功能: 调整之前调用malloc或calloc分配的内存块(传入的ptr为针指向此内存块的指针,如果ptr为NULL,那么realloc的功能就等同于malloc)。如果新的大小大于原始大小,可能会移动内存块到新的位置以提供足够的空间(移动过程也会把内存块中的值相应的拷贝到新的空间)。
            • 示例:
              // 为代码开辟了大小能存放四个整型数据的空间
              int* ptr = (int*)malloc(sizeof(int) * 4);
              // 这个ptr指针,指向内存空间首元素地址
              *ptr = 1;
              *(ptr + 1) = 2;
              *(ptr + 2) = 3;
              *(ptr + 3) = 4;
              //此时空间已经被放满
              ptr = (int*)realloc(ptr, sizeof(int) * 5);
              *(ptr + 4) = 5;
              for (int i = 0; i  
              

              【C/C++】动态内存管理(C:malloc,realloc,calloc,free || C++:new,delete) 第4张

              在上面的示例代码中,使用realloc的方式是极其不安全且不被推荐的,当realloc开辟空间失败时,返回值NULL会让原本ptr指向的空间也丢失掉,所以使用realloc时,应该像下面这样。

              int* ptr = (int*)malloc(sizeof(int) * 4);
              int* tmp = (int*)realloc(ptr, sizeof(int) * 10);
              // 判断是否realloc成功
              if (tmp == NULL) {
              	perror("realloc fail:");
              	exit(1);
              }
              // 此时将tmp赋值给ptr就是安全的
              ptr = tmp;
              

              calloc

              • 头文件:
              • 函数声明: void* calloc(size_t num,size_t size);
              • 功能: 为指定数量的元素分配内存,每个元素的大小在第二个参数中指定,并自动初始化所有位为0。如果分配失败,返回NULL。
              • 示例:
                // 为代码开辟了大小能存放四个整型数据的空间
                // 同时初始化所有位为0
                int* ptr = (int*)calloc(4, sizeof(int));
                for (int i = 0; i  
                

                【C/C++】动态内存管理(C:malloc,realloc,calloc,free || C++:new,delete) 第5张

                free

                • 头文件:
                • 函数声明: void free(void ptr);
                • 功能: 释放之前通过malloc,calloc或realloc分配的内存。一但内存被释放,被释放那块内存就不能再被访问了。
                • 示例:
                  int* ptr1 = (int*)malloc(sizeof(int) * 4);
                  int* ptr2 = (int*)malloc(sizeof(int) * 4);
                  int* ptr2 = (int*)realloc(ptr2, sizeof(int) * 10);
                  int* ptr3 = (int*)calloc(4, sizeof(int));
                  free(ptr1);
                  free(ptr2);
                  free(ptr3);
                  

                  注:

                  在你开辟使用完堆中内存但忘记free释放时,编译器不会报错,但是被开辟的这块空间会一直存在于堆中,无法再次被开辟使用,导致内存泄露。内存泄露在大型项目中是个严重的暗病,不会立即显示,但是非常可能会在项目运行时冷不丁让空间开辟失败导致程序崩溃。

                  同时,不要尝试去释放未经分配的内存块或者多次释放同一个内存块,可能会导致未定义的行为,因此,每个分配的内存块至多free一次。

                  C++中的动态内存管理:new/delete

                  C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。

                  • new:

                    用于在运行时动态的分配内存并返回该内存的指针。它可以与数据类型一起使用,以指定要分配的内存大小和类型。例如,int* p = new int;这行代码会分配一个足以存储int类型数据的内存块,并将返回的地址赋值给指针p。

                  • delete:

                    用于释放由new分配的内存。在C++中,必须显示释放不再需要的内存,以防止内存泄漏。delete运算符接受一个指针作为参数,并释放该指针指向的内存。例如,delete p;这行代码会释放之前由new为p分配的内存。

                    接下来,我们详细讲讲new和delete的用法。

                    new和delete操作内置类型

                    【C/C++】动态内存管理(C:malloc,realloc,calloc,free || C++:new,delete) 第6张

                    申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[]。

                    下面可以来看一组代码案例:

                    // 动态申请一个int类型的空间
                    int* ptr1 = new int;
                    cout 1, 2};
                    for (int i = 0; i 

    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]