【C语言笔记】动态内存管理全解

【C语言笔记】动态内存管理全解

  • 一、为什么要使用动态内存分配?
    • 1、使程序的灵活性更高
    • 2、使空间的使用周期更长及获得更大的空间
  • 二、动态内存函数的介绍和使用及注意事项
    • 1、malloc函数
    • 2、free函数
    • 3、calloc函数
    • 4、realloc函数

一、为什么要使用动态内存分配?

1、使程序的灵活性更高

我们知道在C语言中声明一个数组必须使用一个常量来指定数组的长度,但是数组的长度一般要到具体使用时才能得知,因为它真正所需要的空间取决于输入的数据。
那么当我们不知道数组具体要用多长的时候,一个最简单的方法就是声明一个足够长的数组,以满足有可能出现的最长数组的需要。
这种方法的优势是简单,但缺点也很明显,一是当真正需要的数组长度超过了已申请的长度时,它将无法处理这种情况;二是当实际需要的数组长度较小时,就会造成很大的空间浪费。
这时候就需要我们的动态内存分配了,有了动态内存分配,我们就可以做到想使用多少空间就申请多少空间,这样就方便了很多,且几乎不存在空间浪费。

2、使空间的使用周期更长及获得更大的空间

我们可以简单的把内存空间氛围3个部分:堆区、栈区和静态区。
简单地说一下三者的区别:

堆区: 由malloc系列函数分配内存。其生命周期欧free函数决定。在没有释放之前一直存在,知道程序结束为止,其特点是使用灵活,空间较大,但很容易出错。
栈区: 保存局部变量,栈上的内容只在函数的范围内存在,当函数运行结束,这些内容就会被诶自动销毁。其特点是使用效率高,但空间大小有限,要比堆区少很多。
静态区: 保存自动全局变量和static变量。静态区的内容在整个程序的生命周期内都存在,由编译器在编译时分配。

从上面也可以看出,在堆区上申请的空间的使用周期更长,并且能申请到的空间也比栈区上的要大。

二、动态内存函数的介绍和使用及注意事项

1、malloc函数

C语言为我们提供了一个用于动态内存开辟的函数malloc,当一个程序员额外需要一些内存时,就可以调用malloc函数申请,malloc函数向内存池提取一块适合的内存,并向该程序返回一个指向这块内存的指针。但也有时候会开辟失败,开辟失败时,则会返回一个空指针NULL,所以为了避免对空指针的引用,在调用malloc函数结束之后,对其返回结果做出判断是很重要的。
malloc函数的原型如下:

void* malloc (size_t size);

我们可以看到mallo函数的返回类型是void*,这也就是说malloc并不会事先知道开辟空间的类型或是要用来存放什么类型的数据,所以我们在使用malloc函数时需要根据自己的需要将返回类型强制转化成对应类型的指针。例如我们想要用malloc申请10个整型空间来模拟整型数组,就需要这么写:

int *array = (int*)malloc(10 * sizeof(int));

如果参数size为0,那其行为是标准为定义的,具体怎么处理取决于编译器。

2、free函数

有申请就一定有释放,C语言也给我们提供了一个用于释放动态申请到的空间的函数free,调用这个函数,我们可以释放我们通过动态申请到的空间,但是要注意使用free释放的空间一定要是通过动态申请到的,如果不是,那其行为也是标准为定义的,编译器会报错。
free函数的原型如下:

void free (void* ptr);

使用这个函数我们就只需要传一个参数,就是一个指向动态申请到的空间的起始位置的任意类型的指针。但如果传入的是一个空指针,那什么什么事情也不会发生。
多free函数的使用还有四点要注意的:
第一、 对于动态申请到的空间要么不释放,要么就全部释放,只释放一部分这种行为是标准为定义的。
第二、 对于一块动态分配到的空间不能多次释放。这可以简单的理解为释放了一次就已经还给操作系统了,再释就矛盾了。
第三、 对于每一块动态开辟到的空间,在使用完之后都需要用free释放掉,如果不释放掉就会造成“内存泄漏”。
第四、 用free函数释放后的空就还给操作系统了,如果再使用就是非法访问。所以为了避免对已释放的空间的非法访问,需要在释放空间后将指向这块空间的指针置为空指针。

内存泄漏: 内存泄漏指的是使用完应该被释放掉的空间未被释放,而是这块空间一直被占用。最终会导致内存被一点点的榨干。

3、calloc函数

C语言还给我们提供了另外一个用于动态内存开辟的函数calloc,只不过calloc比malloc多做的一件事是calloc在开辟好空间的同时也将这些空间的内容全都初始化成0,alloc的原型如下:

void* calloc (size_t num, size_t size);

其中参数num和size可以解释为你想为num个大小为size字节的元素开辟空间并初始化为全0。
所以也可以看出calloc比更好的一点是我们可以在开辟的时候就选择好类型了。
既然calloc和malloc都差不过,那么malloc函数使用时应该注意的点对calloc函数也是一样的。

4、realloc函数

有时候我们会发现通过malloc函数或calloc函数申请的空间还是偏大或偏小了,这时候我们就可以用realloc函数来对我们的空间进行再分配,realloc函数的原型如下:

void* realloc (void* ptr, size_t new_size);

这个函数在调用时需要传入一个指向旧空间起始位置的指针和和一个希望调整的新大小,并返回一个指向新空间起始位置的指针。
假设旧的空间大小为old_size,当new_size大于old_size时,realloc函数在调整空间大小的基础上还会将原来空间中的数组原封不动的复制到新空间上。
当new_size小于old_size时,realloc函数就会对原来的空间中的数据进行截取,只截取到新大小的内容复制到新空间。
当然了,realloc也和malloc及calloc一样存在开辟失败的情况,当原有空间之后有足够大的空间时,开辟的方法是直接在原有的空后追加空间。
当原有空间之后没有足够的空间时,开辟的方法是在堆区中另找一块足够大的空间进行再开辟,并将原有空间中的数组复制到新空间中,若找不到足够大的空间则开辟失败,此时就会返回一个空指针,所以对realloc函数的返回结果进行判断也是很重要的。

你可能感兴趣的:(C语言之路,c语言,开发语言)