C语言|动态内存管理(C语言)

目录
一、为什么存在动态内存管理?
二、C语言中动态内存管理方式
1、函数介绍(malloc、calloc、realloc、free)
1)先来看malloc函数
2)calloc函数
3)realloc函数
2、动态内存管理中的错误操作

一、为什么存在动态内存管理?

int var = 10; int arr[10];

上面变量及数组的空间开辟为固定的大小:分别是 4 字节和 40 字节。
1、如果我们在为像栈或是链表这样的数据存储结构开辟空间时,就有可能会在空间不足的情况下为其开辟空间,那么就会涉及到动态内存的开辟。这也就是动态内存管理存在的原因之一。
2、换言之,有的时候我们并不能一次性的开辟好需要的空间大小,如果一次性开辟空间太大会造成空间的浪费,开辟的太小又会不太够用;这时候我们就希望可以有一种动态的方法:当需要使用内存时,需要多少? 立马为其开辟多少,不需要我就不开辟空间,这样既可以满足需求还可以尽可能少的避免造成空间的浪费。
二、C语言中动态内存管理方式 1、函数介绍(malloc、calloc、realloc、free) 前三个函数的使用往往会与free函数搭配起来使用,前者是在内存上开辟所需空间提供使用,后者是在对空间使用结束后释放、归还系统的。
1)先来看malloc函数
void* malloc (size_t size);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
★ 如果开辟成功,则返回一个指向开辟好空间的指针。
★ 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
★ 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自
己来决定。
#include #include int main() { int arr[10] = { 0 }; int* ptr = NULL; ptr = (int*)malloc(10 * sizeof(int)); if (NULL == ptr)//判断ptr指针是否为空 { printf("malloc fail!\n"); exit(-1); } int i = 0; for (i = 0; i<10; i++) { *(ptr + i) = i; } for (i = 0; i<10; i++) { printf("%d ", *(ptr + i)); } printf("\n"); free(ptr); //释放ptr所指向的动态内存 ptr = NULL; return 0; }

C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:
void free (void* ptr);
free函数用来释放动态开辟的内存:(在上面的代码也有所体现)
如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。
2)calloc函数
void* calloc (size_t num, size_t size);
★ 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
★ 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
#include #include int main() { int* ptr = NULL; ptr = (int*)calloc(10 , sizeof(int)); if (NULL == ptr)//判断ptr指针是否为空 { printf("malloc fail!\n"); exit(-1); } int i = 0; for (i = 0; i<10; i++) { printf("%d ", *(ptr + i)); // 全为0 } printf("\n"); free(ptr); //释放ptr所指向的动态内存 ptr = NULL; return 0; }

输出结果: 0 0 0 0 0 0 0 0 0 0
3)realloc函数
realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时
候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。
函数原型如下:
void* realloc (void* ptr, size_t size);
ptr 是要调整的内存地址,size 调整之后新大小,返回值为调整之后的内存起始位置。
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
realloc在调整内存空间的是存在两种情况:
情况1:原有空间之后有足够大的空间
此时扩展内存会在原有空间的后面直接增加,原内存存储数据不变。
#include #include int main() { int* ptr = NULL; ptr = (int*)malloc(10 * sizeof(int)); if (NULL == ptr)//判断ptr指针是否为空 { printf("malloc fail!\n"); exit(-1); } int i = 0; for (i = 0; i<10; i++) { *(ptr + i) = i; } int* ptr1 = NULL; ptr1 = (int*)realloc(ptr, 80); if (NULL == ptr1)//判断ptr指针是否为空 { printf("realloc fail!\n"); exit(-1); } for (i = 0; i<10; i++) { *(ptr1 + i + 10) = i+10; } for (i = 0; i<20; i++) { printf("%d ", *(ptr1 + i)); } printf("\n"); free(ptr1); //释放ptr所指向的动态内存 ptr1 = NULL; return 0; }

上述代码realloc在原来malloc开辟空间的基础上扩展空间,原空间之后有足够大的空间。所以指针ptr和ptr1的地址是相同的。如下图所示:
C语言|动态内存管理(C语言)
文章图片

情况2:原有空间之后没有足够大的空间此时会在堆空间寻找一处足够大小的连续空间,并返回此处空间的地址。
C语言|动态内存管理(C语言)
文章图片

当realloc在原来空间的大小上新开辟的空间太大时,即原有空间之后没有足够大的空间 ;此时会在堆空间寻找一处足够大小的连续空间,并返回此处空间的地址。同时也会释放原空间。(将上面代码段中realloc开辟空间的大小调的大点可以验证)
2、动态内存管理中的错误操作 在动态内存管理中所开辟的空间使用结束后需要对其进行释放操作,目的是为了将空间还给内存供系统调用。但需要注意以下错误操作:
1)对NULL进行解引用。
2)越界访问开辟的空间。
int main() { int* ptr = NULL; ptr = (int*)malloc(10 * sizeof(int)); int i = 0; for (i = 0; i < 10; i++) { *(ptr + i) = i; } printf("%d\n", *(ptr + 5)); // 正确访问 printf("%d\n", *(ptr + 15)); // 越界访问 return 0; }

3)对非动态开辟的空间进行free操作。
4)只释放部分动态开辟空间。
int main() { int* ptr = NULL; ptr = (int*)malloc(10 * sizeof(int)); int i = 0; for (i = 0; i < 10; i++) { *(ptr + i) = i; } ptr++; free(ptr); ptr = NULL; return 0; }

5)对同一动态开辟的内存空间多次释放。
6)忘记释放动态开辟的内存空间。(内存泄漏)
结论:动态开辟的内存空间使用结束后一定要释放,并且要正确释放。

【C语言|动态内存管理(C语言)】★★★★★ 感谢阅读!!!

    推荐阅读