动态内存分配

28 阅读3分钟

栈、堆、静态区

image-20230228100831844.png

动态内存函数

  • malloc和free

    • 函数原型

      void* malloc( size_t size );
      
    • 使用

      #include <stdlib.h>
      #include <string.h>
      #include <errno.h>
      #include <stdio.h>
      int main() {
          // 申请10个整型空间
          // int* p = (int*)malloc(10 * sizeof(int));
          int* p = (int*)malloc(INT_MAX);
          // 分配失败
          if (p == NULL) {
              printf("%s\n", strerror(errno));
          } else {  // 正常使用
              for (int i = 0; i < 10; i++) {
                  *(p + i) = i;
              }
              for (int i = 0; i < 10; i++) {
                  printf("%d\t", *(p + i));
              }
          }
          // 释放空间
          // free用来释放动态开辟的空间
          free(p);
          p = NULL;  
          return 0;
      }
      
  • calloc

    • 函数原型

      void* calloc( size_t num, size_t size );
      
    • 使用

      #include <stdlib.h>
      #include <string.h>
      #include <errno.h>
      #include <stdio.h>
      int main() {
          // 申请10个整型空间
          int* p = (int*)calloc(10, sizeof(int));
          // 分配失败
          if (p == NULL) {
              printf("%s\n", strerror(errno));
          } else {  // 正常使用
              for (int i = 0; i < 10; i++) {
                  *(p + i) = i;
              }
              for (int i = 0; i < 10; i++) {
                  printf("%d\t", *(p + i));
              }
          }
          // 释放空间
          // free用来释放动态开辟的空间
          free(p);
          p = NULL;  
          return 0;
      }
      
  • malloc和calloc的区别

    • calloc在返回申请好的空间地址之前,会给申请的空间初始化为0
    • 而malloc则不会进行初始化操作,相对来说,malloc的效率略高
  • realloc

    • 函数原型

      void* realloc( void* memblock, size_t size );
      
    • 使用

      int main() {
          int* p = (int*)malloc(20);
          if (p == NULL) {
              printf("%s\n", strerror(errno));
          } else {
              for (int i = 0; i < 5; i++) {
                  *(p + i) = i;
              }
              for (int i = 0; i < 5; i++) {
                  printf("%d\t", *(p + i));
              }
          }
          printf("\n");
          // realloc使用的注意事项
          p = (int*)realloc(p, 40);  // 有很大风险
          for (int i = 5; i < 10; i++) {
              *(p + i) = i;
          }
          for (int i = 0; i < 10; i++) {
              printf("%d\t", *(p + i));
          }
          free(p);
          p = NULL;
          return 0;
      }
      
    • 注意事项

      • 如果p指向的空间后面有足够的内存可以追加,则直接追加,返回p

      • 如果p指向的空间后面没有足够的内存可以追加,则新开辟一块满足要求的空间,并且把原来内存中的数据拷贝回来,释放旧的内存空间,最后返回新开辟内存空间的地址

      • 要用一个新的变量来接收realloc函数的返回值

        p = (int*)realloc(p, INT_MAX);
        
        • 假如此时用原来的p来接收realloc的返回值,如果realloc分配内存失败,则会返回NULL,那么,原本p指向的空间还有数据,但是现在就变成了没有数据,所以要用一个新的变量来接收realloc的返回值

          int* p = (int*)malloc(20);
              if (p == NULL) {
                  printf("%s\n", strerror(errno));
              } else {
                  for (int i = 0; i < 5; i++) {
                      *(p + i) = i;
                  }
                  for (int i = 0; i < 5; i++) {
                      printf("%d\t", *(p + i));
                  }
              }
              printf("\n");
          int* ptr = (int*)realloc(p, INT_MAX);
          if (ptr != NULL) {
              p = ptr;
          }
          for (int i = 5; i < 10; i++) {
                  *(p + i) = i;
              }
              for (int i = 0; i < 10; i++) {
                  printf("%d\t", *(p + i));
              }
          free(p);
          p = NULL;
          return 0;
          // 疑问?
          // free(p);
          // 上述代码中,如果原空间后面有足够的内存去追加,则realloc返回的是原空间的地址,用完之后free(p),可以理解
          // 但是如果原空间后面没有足够的内存可以分配,那么realloc返回的是新开辟空间的地址,这时候
          p = ptr;
          free(p);
          // 我们改变了p的指向,最后释放的是新开辟空间的内存,那么原来的内存空间是由系统自动释放了吗?这里希望评论区的大神给一个回复
          

动态内存开辟的常见错误

  • 没有对malloc的返回值进行判断

    int main() {
        int* p = (int*)malloc(100000000000000000000000000000);
        // 要对malloc函数的返回值进行判断
        for (int i = 0; i < 5; i++) {
            *(p + i) = i;
        }
        free(p);
        p = NULL;
        return 0;
    }
    
  • 对动态开辟的空间越界访问

    int main() {
        int* p = (int*)malloc(10 * sizeof(int));
        for (int i = 0; i < 20; i++) {
            *(p + i) = i;
        }
        free(p);
        p = NULL;
        return 0;
    }
    
  • 使用free释放非动态开辟的空间

    int main() {
        int arr[10] = {0};
        int* p = arr;
        free(p);
        p = NULL;
        return 0;
    }
    
  • 使用free释放动态内存的一部分

    int main() {
        int*p = (int*)malloc(10 * sizeof(int));
        if (p == NULL)
            return 1;
        for (int i = 0; i < 5; i++) {
            *p++ = i;
        }
        free(p);
        p = NULL;
        return 0;
    }
    // 两个风险
    // 释放的是当前p指向之后的空间,本身就不允许
    // 再也找不到p最初的指向了
    
  • 对同一块动态内存重复释放

    int main() {
        int* p = (int*)malloc(100);
        // 使用
        // 释放
        free(p);
        // 释放
        free(p);
        return 0;
    }
    // 解决方法
    // 第一次释放空间后,将p = NULL
    
  • 动态开辟的空间忘记释放