您现在的位置是:首页 >技术交流 >【C语言】动态内存管理网站首页技术交流

【C语言】动态内存管理

Siestaaaa 2024-06-17 10:32:06
简介【C语言】动态内存管理

一、为什么存在动态内存分配

我们已经知道的一些内存开辟方式有:

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

上述方式开辟的内存大小是固定的,而实际需要的空间大小有时在程序运行过程才能知道。于是便有了动态内存分配,也就是在堆上动态申请内存。

二、动态内存函数

下面我们要介绍的动态内存函数有:malloccallocfreerealloc,这些函数都在stdlib.h头文件中声明。

1. malloc

void* malloc (size_t size);
  • 用于向内存申请一块连续可用的空间。
  • 申请成功,函数返回的是这块空间的起始地址。
  • 申请失败,函数返回的是空指针。
  • 返回值类型是void*,这种类型的指针不知道步长,也不能解引用。所以函数的返回值要手动强转成需要的类型,再接收检查是否为空指针。
  • 参数size是要开辟多少个字节的空间。

2. calloc

void* calloc (size_t num, size_t size);
  • 类似malloc,作用是为num个大小为size的元素开辟一块空间。
  • 不同的是calloc会把每个字节都初始化为0,而malloc函数不会。

3. free

动态内存分配是在堆区上开辟空间的,它不会自动销毁,需要自己回收,或者程序结束时自动回收。如果不及时回收动态内存,会造成很多严重的后果,如内存泄露。
所以C语言提供了一个函数,用于回收动态分配的内存。

void free (void* ptr);
  • 用于释放动态内存。
  • ptr是要释放的空间的起始地址。
  • 如果ptr指向的空间不是动态开辟的,函数的行为是未定义的。
  • 如果ptr是空指针,函数什么事也不做。
  • free释放空间后,我们需要手动将ptr置空,防止出现野指针。

4. realloc

有时候我们发现之前申请的空间太小了,会对内存的大小进行调整。
所以C语言提供了一个函数,用于对动态内存的大小进行调整。

void* realloc (void* ptr, size_t size);
  • ptr是要调整的内存起始地址,size是我们期望调整之后的内存大小。
  • 调整成功,函数返回的是调整之后的内存起始地址。
  • 调整失败,函数返回的是空指针。
  • realloc调整内存空间,有两种情况:
    • 原空间后面有足够大的空间
      • 直接在原有内存后面追加空间,原空间的数据不会发生变化。
    • 原空间后面没有足够大的空间
      • 此时要扩展内存,就会另找一个大小合适的连续空间来使用。
        原空间的数据将拷贝到新空间中,然后原空间内存的使用权会被回收。

来看一段代码:

    //正常开辟动态内存
    int *ptr = (int*)malloc(100);
    //假设开辟成功
    //下面要扩展容量

    //错误做法
    ptr = (int*)realloc(ptr, 1000);
    
    //正确做法
    int*p = (int*)realloc(ptr, 1000);
    if(p != NULL)
    {
        ptr = p;
    }

错误做法:
我们想用上面接收了malloc返回值的指针ptr来接受realloc的返回值。这样如果realloc调整失败,ptr将接收到一个空指针。这样我们就会丢失malloc开辟的内存空间的地址,导致后面无法释放malloc开辟的内存,造成内存泄漏。

正确做法:
另外定义一个指针变量p来接收realloc的返回值。先判断p不是空指针,保证realloc调整成功,返回的是调整好的空间地址。如果realloc另找了一个大小合适的空间来使用,旧空间也会自动回收。现在就可以安心将p的值赋给ptr了。

类似坑还有很多,我们只需注意一下几点:
• 动态开辟的内存空间,最后一定要释放。
• 想要释放内存,便要保证存放该内存地址的指针还存在。
• 释放完内存后,指针必须置空,防止非法访问。

三、常见错误

  1. 对空指针解引用。
  2. 越界访问。
  3. 非动态开辟的内存用free释放。
  4. 只释放了一块动态内存的一部分。
  5. 对同一块动态内存重复释放。
  6. 动态开辟的内存忘记释放。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。