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


          相关文章
          |
          6天前
          |
          C语言
          【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
          【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
          16 0
          |
          3天前
          |
          程序员 C语言
          C语言内存管理:malloc、calloc、realloc与free函数详解
          C语言内存管理:malloc、calloc、realloc与free函数详解
          4 0
          |
          14天前
          |
          消息中间件 存储 Kafka
          实时计算 Flink版产品使用问题之 从Kafka读取数据,并与两个仅在任务启动时读取一次的维度表进行内连接(inner join)时,如果没有匹配到的数据会被直接丢弃还是会被存储在内存中
          实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
          |
          6天前
          |
          存储 Java C++
          Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
          Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
          18 2
          |
          10天前
          |
          存储
          数据在内存中的存储(2)
          数据在内存中的存储(2)
          24 5
          |
          10天前
          |
          存储 小程序 编译器
          数据在内存中的存储(1)
          数据在内存中的存储(1)
          27 5
          |
          10天前
          |
          存储 安全 Java
          SpringSecurity6从入门到实战之初始用户如何存储到内存
          Spring Security 在 SpringBoot 应用中默认使用 `UserDetailsServiceAutoConfiguration` 类将用户信息存储到内存中。当classpath有`AuthenticationManager`、存在`ObjectPostProcessor`实例且无特定安全bean时,此配置生效。`inMemoryUserDetailsManager()`方法创建内存用户,通过`UserDetails`对象填充`InMemoryUserDetailsManager`的内部map。若要持久化到数据库,需自定义`UserDetailsService`接口实
          |
          9天前
          |
          存储 编译器 C语言
          数据在内存中的存储
          数据在内存中的存储
          17 2
          |
          9天前
          |
          存储
          数据在内存中的存储(了解数据在内存中的存储规则,看这一篇就够了!)
          数据在内存中的存储(了解数据在内存中的存储规则,看这一篇就够了!)
          |
          17天前
          |
          存储 C++
          C primer plus 学习笔记 第12章 存储类别、链接和内存管理
          C primer plus 学习笔记 第12章 存储类别、链接和内存管理