28 动态内存分配

1 动态内存分配的意义

  • C语言中的一切操作都是基于内存

  • 变量数组都是内存的别名

    • 内存分配由编译器在编译期间决定
    • 定义数组的时候必须指定数组长度
    • 数组长度是在编译期就必须确定
  • 需求:程序运行的过程中,可能需要使用一些额外的内存空间

2 malloc 和 free

  • mallocfree 用于执行动态分配内存和释放

  • malloc 所分配的是一块连续的内存

  • malloc字节为单位,并且不带任何的类型信息

  • free 用于将动态内存归还系统

    void* malloc(size_t size);
    void free(void* pointer);
    
  • 注意

    • mallocfree库函数,而不是系统调用
    • malloc 实际分配的内存可能会比请求的多
    • 不能依赖于不同平台下的 malloc 行为——为了移植性
    • 当请求的动态内存无法满足时,malloc 返回 NULL
    • free 的参数为 NULL 时,函数直接返回
  • 问题1:malloc(0); 将会返回什么?

    • Demo

      #include <stdio.h>
      #include <malloc.h>
      
      int main()
      {
          int* p  = (int*)malloc(0);
      
          printf("p = %p
      ",p);
          
          free(p);
      
          return 0;
      }
      
    • 运行结果 => malloc(0)是合法的,但申请的内存长度为0

      p = 0x895a008
      
  • 问题2:如果不断地执行 malloc(0); 但是不进行内存释放,会产生内存泄漏么?

    • 会:malloc 实际分配的内存可能会比请求的多malloc(0); 可能得到的内存是 4 个字节大小
  • 实例:内存泄漏检测模块

    • Demo

      #include <stdio.h>
      #include "mleak.h"
      
      void f()
      {
          MALLOC(100);  //自定义的malloc函数,这里没有进行内存释放
      }
      
      int main()
      {
          int* p = (int*)MALLOC(3 * sizeof(int));
          
          f();
          
          p[0] = 1;
          p[1] = 2;
          p[2] = 3;
          
          FREE(p);  //自定义的free函数
          
          PRINT_LEAK_INFO();  //打印当前内存泄漏的信息
          
          return 0;
      }
      
      
      //mleak.h
      #ifndef _MLEAK_H_
      #define _MLEAK_H_
      
      #include <malloc.h>
      
      #define MALLOC(n) mallocEx(n, __FILE__, __LINE__)
      #define FREE(p) freeEx(p)
      
      void* mallocEx(size_t n, const char* file, const line);
      void freeEx(void* p);
      void PRINT_LEAK_INFO();
      
      #endif
      
      //mleak.c
      #include "mleak.h"
      
      #define SIZE 256
      
      /* 动态内存申请参数结构体 */
      typedef struct
      {
          void* pointer;
          int size;
          const char* file;
          int line;
      } MItem;
      
      static MItem g_record[SIZE]; /* 记录动态内存申请的操作 */
      
      void* mallocEx(size_t n, const char* file, const line) 
      {
          void* ret = malloc(n); /* 动态内存申请 */
          
          if( ret != NULL )
          {
              int i = 0;
              
              /* 遍历全局数组,记录此次操作 */
              for(i=0; i<SIZE; i++)
              {
                  /* 查找位置 */
                  if( g_record[i].pointer == NULL )
                  {
                      g_record[i].pointer = ret;
                      g_record[i].size = n;
                      g_record[i].file = file;
                      g_record[i].line = line;
                      break;
                  }
              }
          }
          
          return ret;
      }
      
      void freeEx(void* p)
      {
          if( p != NULL )
          {
              int i = 0;
              
              /* 遍历全局数组,释放内存空间,并清除操作记录 */
              for(i=0; i<SIZE; i++)
              {
                  if( g_record[i].pointer == p )
                  {
                      g_record[i].pointer = NULL;
                      g_record[i].size = 0;
                      g_record[i].file = NULL;
                      g_record[i].line = 0;
                      
                      free(p);
                      
                      break;
                  }
              }
          }
      }
      
      void PRINT_LEAK_INFO()
      {
          int i = 0;
          
          printf("Potential Memory Leak Info:
      ");
          
          /* 遍历全局数组,打印未释放的空间记录 */
          for(i=0; i<SIZE; i++)
          {
              if( g_record[i].pointer != NULL )
              {
                  printf("Address: %p, size:%d, Location: %s:%d
      ", g_record[i].pointer, g_record[i].size, g_record[i].file, g_record[i].line);
              }
          }
      }
      
    • 运行

      
      

3 calloc 和 realloc

  • malloc 的同胞兄弟

    void* calloc(size_t num,size_t size);
    void* realloc(void* pointer,size_t new_size);
    
  • calloc参数代表所返回内存的类型信息

  • calloc 会将返回的内存初始化为0

  • realloc 用于修改一个原先已经分配的内存块大小

    • 在使用 realloc 之后应该使用其返回值
    • pointer 的**第一个参数为 NULL 时,等价于 malloc **
  • 对比

    • malloc 单纯地从系统中申请固定字节大小的内存
    • calloc 能以类型大小为单位申请内存并初始化为0
    • realloc 用于重置内存大小,如果内存重置为一个更大的内存空间,扩充的部分为随机值
  • 示例:callocrealloc 的使用

    • Demo

      #include <stdio.h>
      #include <malloc.h>
      
      #define SIZE 5
      
      int main()
      {
          int i = 0;
          int* pI = (int*)malloc(SIZE * sizeof(int));
          short* pS = (short*)calloc(SIZE, sizeof(short));
          
          for(i=0; i<SIZE; i++)
          {
              printf("pI[%d] = %d, pS[%d] = %d
      ", i, pI[i], i, pS[i]);
          }
          
          printf("Before: pI = %p
      ", pI);
          
          pI = (int*)realloc(pI, 2 * SIZE * sizeof(int));
          
          printf("After: pI = %p
      ", pI);
          
          for(i=0; i<10; i++)
          {
              printf("pI[%d] = %d
      ", i, pI[i]);
          }
          
          free(pI);
          free(pS);
          
          return 0;
      }
      
    • GCC 编译运行

      pI[0] = 0,pS[0] = 0
      pI[1] = 0,pS[1] = 0
      pI[2] = 0,pS[2] = 0
      pI[3] = 0,pS[3] = 0
      pI[4] = 0,pS[4] = 0
      Before : pI = 0x9241008
      After : pI = 0x9241438
      pI[0] = 0
      pI[1] = 0
      pI[2] = 0
      pI[3] = 0
      pI[4] = 0
      pI[5] = 0
      pI[6] = 0
      pI[7] = 0
      pI[8] = 0
      pI[9] = 0
      
    • VS 编译运行

      pI[0] = -842150451, pS[0] = 0
      pI[1] = -842150451, pS[1] = 0
      pI[2] = -842150451, pS[2] = 0
      pI[3] = -842150451, pS[3] = 0
      pI[4] = -842150451, pS[4] = 0
      Before: pI = 014B5650
      After: pI = 014B5650
      pI[0] = -842150451
      pI[1] = -842150451
      pI[2] = -842150451
      pI[3] = -842150451
      pI[4] = -842150451
      pI[5] = -842150451
      pI[6] = -842150451
      pI[7] = -842150451
      pI[8] = -842150451
      pI[9] = -842150451
      
原文地址:https://www.cnblogs.com/bky-hbq/p/13773697.html