为什么要进行动态内存管理?
在平常进行变量定义的时候,我们通常是进行如下的定义方式:
cppint a = 10;//整型变量 int arr[] = {1,2,3,4,5,6,7,8,9,10};//十个元素的整型数组
但是这样定义出的变量a只能进行存储一个整形变量,以及数组arr也只能存储十个整形,如果想要扩大,那是做不到的,只能重新定义一个,这是相当麻烦的,所以我们便引出了动态内存,可以根据需要来进行扩大,这样不仅节约了空间,也提供了便利。
动态内存函数的介绍
malloc函数:
cpp#include<stdlib.h> //这是包含malloc的头文件 int main() { void* p = malloc(10 * sizeof(int)); //这种当然没什么问题,但是使用者在用的时候肯定是知道自己要开辟什么类型的空间的, // 如果用void* P来接收的话,反而不是太方便,所以我们习惯写下面这种写法。 int* p = (int*)malloc(10 * sizeof(int)); if (p == NULL) {//这里我们需要判断一下是否申请成功了, //如果不进行判断的话,后面会出现解引用NULL指针的问题。 perror("malloc"); return 1; } return 0; }
那么有人要问了,malloc真的会申请失败么,答案是会的 🕶️🕶️🕶️
看图当我们所申请的值太大时,就会申请失败。
free函数:
当我们动态调用之后,我们有义务对其动态开辟的空间进行释放,也就是说我们向操作系统调了一块空间,我们就必须在不用的时候返还给操作系统,所以free函数是用来将动态开辟的空间释放的。
cpp#include<stdio.h> #include<stdlib.h> #include<limits.h> int main() { int* p1 = (int*)malloc(10 * sizeof(int)); if (p1== NULL) { perror("malloc"); return 1; } for (int i = 0; i < 10; i++) { *(p1 + i) = i; printf("%d ", *(p1+i)); } free(p1); p1 = NULL; return 0; }
我在14行处打一个断点, F5跳到断点处
可以看到,地址并没有发生任何变化,但是实际上这块地址已经还给操作系统 了,不再属于我们动态开辟的了,所以一定要在释放完之后,进行指针置空,防止后面访问野指针而导致程序错误。
calloc函数
这是malloc函数的空间开辟,可以看到并没有进行初始化
这是改用calloc函数进行的:
cpp#include<stdio.h> #include<stdlib.h> int main() { int* p1 = (int*)calloc(10 , sizeof(int)); if (p1== NULL) { perror("calloc"); return 1; } for (int i = 0; i < 10; i++) { printf("%d\n", *(p1+i)); } free(p1); p1 = NULL; return 0; }
realloc函数:
那么正确的方法应该是这样的:
这样即使realloc申请失败,那也不会影响原本的内存内容。
当然如果我们在realloc的地址处传一个NULL指针的话,其实它可以等价于malloc
cppint* p = (int*)realloc(NULL, 20*sizeof(int));// == malloc
常见的动态内存错误
对空指针进行解引用操作
cppint* p = (int*)malloc(40); //如果不对p进行返回值判断,那么当申请失败时,就会发生解引用操作 *p = 20;
对动态空间的越界访问
当我们的访问超出了申请的内存范围时,就是越界了,这时我们的编译器就会报错。
对非动态开辟内存使用free释放
cpp#include<stdio.h> #include<stdlib.h> int main() { int a = 10; int* p = &a; free(p); p = NULL; return 0; }
我们不能对局部变量进行free释放,free函数只能用于malloc、calloc等这些存在于堆上的内存。
使用free释放一块动态开辟内存中的一部分
cpp#include<stdio.h> #include<stdlib.h> int main() { int* p = (int*)calloc(10, sizeof(int)); if (p == NULL) { perror("calloc"); return 1; } int i = 0; for (i = 0; i < 5; i++) { *p = i; p++; } //0 1 2 3 4 0 0 0 0 0 //释放 free(p); p = NULL; return 0; }
如果我们在这个代码里,p进行++操作,当p+到5的时候,突然不想用了,那么我们是不能进行释放一部分动态开辟出来的空间的。
对同一块动态内存多次释放
cpp#include<stdio.h> #include<stdlib.h> int main() { int* p = (int*)malloc(40); if (p == NULL) { //.... return 1; } //.... //释放 free(p); p = NULL; //... free(p); return 0; }
这里我们对p进行了两次释放,就会导致出错。
动态开辟内存忘记释放(内存泄漏)
cppvoid test() { int* p = (int*)malloc(40); //... if (3) return; free(p); p = NULL; } int main() { test(); //... while (1) { ; } return 0; }
这里我们在主函数中调用了test,但是进入函数malloc了空间,代码走到if时直接就返回主函数了,并没有走到free,然后主函数里面也无法对其进行释放,如果我们的程序不结束,就例如这里的while死循环,那么malloc出的空间就存在了内存泄漏。
下次我在给大家带来一些常见的经典题目🌹🌹🌹