动态内存分配—malloc calloc realloc free 和常见动态内存错误

简介: 我们熟知的内存开辟方式有两种:第一种便是通过等关键字为变量开辟空间;第二种便是通过开辟一段连续的空间。这样开辟的空间很局限:1. 空间开辟大小是固定的。2. 数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配。动态内存分配所开辟的空间在堆区,并且是可以扩容的。

 我们熟知的内存开辟方式有两种:第一种便是通过“int”等关键字为变量开辟空间;第二种便是通过数组开辟一段连续的空间。如下:

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

image.gif

但是这样开辟的空间有俩个特点:

1. 空间开辟大小是固定的。

2. 数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配

但有的时候我们所需要的空间大小在程序运行过程中是会改变的,那数组在编译时开辟的空间便不能满足我们的需求了。这时便需要我们的动态内存分配了。

动态内存分配所开辟的空间是在堆中开辟的:

局部变量 函数形参 栈区

动态内存分配所开辟的空间

malloc realloc calloc

堆区

全局变量 静态变量

静态区

目录

一.malloc和free

1.1 malloc

1.2 free

1.3 malloc和free的使用

二.calloc

2.1 calloc

2.2 calloc的使用

三.realloc

3.1 realloc

3.2  realloc在调整内存空间的是存在两种情况

3.2.1原有空间之后有足够大的空间:

3.2.2.原有空间之后没有足够大的空间:

3.3 realloc的使用

四.常见的动态内存错误

4.1 对NULL指针的解引用操作

4.2 对动态开辟空间的越界访问

4.3 对非动态开辟内存使用free释放

4.4 使用free释放一块动态开辟内存的一部分

4.5 对同一块动态内存多次释放

4.6 动态开辟内存忘记释放(内存泄漏)


一.malloc和free

函数使用前需包含头文件 #include <stdlib.h>

1.1 malloc

void* malloc (size_t size);

该函数中的参数(size_t size)是我们所需要开辟的空间大小。

这个函数会向堆内存中申请一块连续可用的空间,并返回指向这块内存的指针

    • 如果开辟成功,则返回一个指向开辟空间的指针。
    • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
    • 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己
    • 来决定。
    • 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

    1.2 free

    void free (void* ptr);

    动态内存所开辟的空间在使用过后必须释放,否则可能会出现内存泄漏的问题。而free函数专门用来释放动态开辟的内存空间

      • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
      • 如果参数 ptr 是NULL指针,则函数什么事都不做

      1.3 malloc和free的使用

      //代码示例
      #include<stdio.h>
      #include<stdlib.h>
      int main()
      {
        int* ptr = NULL;
        ptr = (int*)malloc(8 * sizeof(int));
        if (ptr == NULL)
        {
          printf("空间申请失败");
        }
        else
        {
          printf("空间申请成功");
        }
        free(ptr);//释放malloc申请的空间
        ptr = NULL;
        return 0;
      }

      image.gif

      二.calloc

      void* calloc (size_t num, size_t size);

      2.1 calloc

      C语言还提供了一个函数叫做calloc,calloc也用于动态内存分配:

        • 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
        • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

        2.2 calloc的使用

        //代码示例:
        #include <stdio.h>
        #include <stdlib.h>
        int main()
        {
          int* p = (int*)calloc(10, sizeof(int));
          if (NULL != p)
          {
            //使用空间
          }
          free(p);  //释放空间
          p = NULL;
          return 0;
        }

        image.gif

        image.gif编辑

        当我们需要对申请的空间进行初始化的时候我么可以使用calloc

        三.realloc

        void* realloc (void* ptr, size_t size);

        3.1 realloc

          • realloc函数的出现让动态内存管理更加灵活。
          • 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的使用内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。
          • ptr 是要调整的内存地址
          • size 调整之后新大小
          • 返回值为调整之后的内存起始位置。
          • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 新 的空间。

          3.2  realloc在调整内存空间的是存在两种情况

          3.2.1原有空间之后有足够大的空间:

          如果原有空间之后有足够大的空间时,realloc在扩展空间的时候会在原有空间之后追加,原来空间的数据不发生变化。

          image.gif编辑

          3.2.2.原有空间之后没有足够大的空间:

          如果原有空间之后没有足够大的空间时,realloc在扩展空间的时候会在堆内存中重新找到一块足够大的空间,原来空间的数据会被拷贝到新的空间中去。 最后返回新空间的内存地址。

          image.gif编辑

          3.3 realloc的使用

          代码示例:

          #include <stdio.h>     
          #include <stdlib.h>     
          int main()
          {
              int input, n;
              int count = 0;
              int* numbers = NULL;
              int* more_numbers = NULL;
              do {
                  printf("Enter an integer value (0 to end): ");
                  scanf("%d", &input);
                  count++;
                  more_numbers = (int*)realloc(numbers, count * sizeof(int));
                  if (more_numbers != NULL) {
                      numbers = more_numbers;
                      numbers[count - 1] = input;
                  }
                  else {
                      free(numbers);
                      puts("Error (re)allocating memory");
                      exit(1);
                  }
              } while (input != 0);
              printf("Numbers entered: ");
              for (n = 0; n < count; n++) printf("%d ", numbers[n]);
              free(numbers);
              return 0;
          }

          image.gif

          image.gif编辑

          四.常见的动态内存错误

          4.1 对NULL指针的解引用操作

          //错误示范
          void test()
          {
              int *p = (int *)malloc(INT_MAX/4);
              *p = 20;//如果p的值是NULL,就会有问题
              free(p);
          }

          image.gif

          4.2 对动态开辟空间的越界访问

          //错误示范
          void test()
          {
              int i = 0;
              int *p = (int *)malloc(10*sizeof(int));
              if(NULL == p)
              {
                  exit(EXIT_FAILURE);
              }
              for(i=0; i<=10; i++)
              {
                  *(p+i) = i;//当i是10的时候越界访问
              }
              free(p);
          }  C

          image.gif

          4.3 对非动态开辟内存使用free释放

          void test()
          {
              int a = 10;
              int *p = &a;
              free(p);//ok?
          }

          image.gif

          4.4 使用free释放一块动态开辟内存的一部分

          void test()
          {
              int *p = (int *)malloc(100);
              p++;
              free(p);//p不再指向动态内存的起始位置
          }

          image.gif

          4.5 对同一块动态内存多次释放

          void test()
          {
              int *p = (int *)malloc(100);
              free(p);
              free(p);//重复释放
          }

          image.gif

          4.6 动态开辟内存忘记释放(内存泄漏)

          void test()
          {
              int *p = (int *)malloc(100);
              if(NULL != p)
              {
                  *p = 20;
              }
          }
          int main()
          {
              test();
              while(1);
          }

          image.gif


          相关文章
          |
          1月前
          |
          程序员 C++ 容器
          在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
          在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
          |
          1月前
          |
          编译器 C语言 C++
          详解C/C++动态内存函数(malloc、free、calloc、realloc)
          详解C/C++动态内存函数(malloc、free、calloc、realloc)
          176 1
          |
          1月前
          一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
          一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
          70 0
          |
          4月前
          |
          存储 分布式计算 Hadoop
          HadoopCPU、内存、存储限制
          【7月更文挑战第13天】
          288 14
          |
          3月前
          |
          存储 编译器 C语言
          【C语言篇】数据在内存中的存储(超详细)
          浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
          378 0
          |
          26天前
          |
          存储 C语言
          数据在内存中的存储方式
          本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
          55 1
          |
          30天前
          |
          存储
          共用体在内存中如何存储数据
          共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
          |
          1月前
          |
          存储 弹性计算 算法
          前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
          本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
          |
          1月前
          |
          存储 编译器
          数据在内存中的存储
          数据在内存中的存储
          42 4
          |
          1月前
          |
          存储 Java
          JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
          这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
          53 0
          JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配