动态内存分配—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


          相关文章
          |
          3月前
          |
          程序员 C++ 容器
          在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
          在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
          |
          3月前
          |
          编译器 C语言 C++
          详解C/C++动态内存函数(malloc、free、calloc、realloc)
          详解C/C++动态内存函数(malloc、free、calloc、realloc)
          440 1
          |
          3月前
          一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
          一刻也没有为它哀悼~接下来登场的是动态内存分配的malloc与realloc以及free函数
          86 0
          |
          2月前
          |
          缓存 Prometheus 监控
          Elasticsearch集群JVM调优设置合适的堆内存大小
          Elasticsearch集群JVM调优设置合适的堆内存大小
          342 1
          |
          28天前
          |
          存储 监控 算法
          深入探索Java虚拟机(JVM)的内存管理机制
          本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
          |
          2月前
          |
          Java
          JVM内存参数
          -Xmx[]:堆空间最大内存 -Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的 -Xmn[]:新生代的最大内存 -xx[use 垃圾回收器名称]:指定垃圾回收器 -xss:设置单个线程栈大小 一般设堆空间为最大可用物理地址的百分之80
          |
          2月前
          |
          Java
          JVM运行时数据区(内存结构)
          1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
          25 3
          |
          2月前
          |
          存储 缓存 监控
          Elasticsearch集群JVM调优堆外内存
          Elasticsearch集群JVM调优堆外内存
          54 1
          |
          2月前
          |
          Arthas 监控 Java
          JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
          本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
          |
          3月前
          |
          缓存 算法 Java
          JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
          这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
          105 4
          JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS

          热门文章

          最新文章