动态内存管理的优点
因为动态内存函数malloc,calloc,realloc开辟的都是一块连续的空间,所以我们也可以把动态内存开辟的空间看作一个数组,那么动态内存开辟空间与数组相比有什么优点呢?
我们都知道在C99标准之前,C语言是不支持变长数组的,我们只能在定义数组的时候就给定数组的大小,并且数组开辟的空间大小是固定的,所以就出现了动态内存函数来分配内存。动态内存开辟空间可以开辟任意空间的大小(在你计算机内存够的时候),并且后面还可以使用动态内存函数来修改你前面开辟的空间大小,这就为我们带来了极大的便利。
知道了动态内存管理的优点,那么我们就来看看动态开辟内存的时候需要用到哪些函数吧。注意动态内存函数需要的头文件是#include<stdlib.h>,我下面就不过多赘述了。
malloc函数和free
void* malloc (size_t size);
malloc函数的作用是开辟指定大小字节的空间,它所开辟的内存是在堆区,而我们平常的局部变量和函数的形式参数是在栈区的,当你出了作用域或函数时就会被销毁,而你堆区上的变量如果你不专门释放就会一直存在,直到你的程序结束。
malloc函数的参数是无符号整型,返回值是void*,如果malloc函数开辟成功则返回开辟的空间的地址,开辟失败则返回null指针。所以我们就需要用一个指针来接收返回值,并且判断是否开辟成功。
int* ptr = NULL; ptr = (int*)malloc(40);
这里因为我们既然开辟了空间,我们就需要用的,但是空指针又不能被访问,所以我们可以直接将malloc的返回值强制类型转换为我们需要的类型,并用该类型的指针来接收。但是如果我这样写,会有什么结果呢?
int *ptr = NULL; ptr = (int*)malloc(0);
这样malloc会开辟多少空间呢?其实这个并不确定,因为这个行为是标准是未定义的,取决于编译器。
我们既然开辟了空间,但是如果我们不想用了该怎么办呢?这里就需要free函数来释放动态内存函数开辟的空间了。
void free (void* ptr);
int* ptr = NULL; ptr = (int*)malloc(num*sizeof(int)); if(NULL != ptr)//判断ptr指针是否为空 { int i = 0; for(i=0; i<num; i++) { *(ptr+i) = 0; } } free(ptr);//释放ptr所指向的动态内存 ptr = NULL;//是否有必要? return 0;
记住了,这里free函数只是将动态开辟的空间释放掉,还给操作系统,他是不会把这个指针置为空指针的,所以这里需要我们自己手动将指针置为空指针,防止出现野指针。
calloc函数
当我们在定义数组的时候呢,我们最好可以将数组进行初始化,那么我们动态开辟的内存也是如此,我们也需要将开辟的空间进行初始化。这里就出现了calloc函数,他的作用是将开辟的空间初始化为0。
void* calloc (size_t num, size_t size);
calloc函数的参数一个是元素的个数,一个是每个元素的大小,单位是字节,返回值同样是void*,所以使用方法跟前面的malloc差不多,我们来看看。
#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> int main() { int* p = NULL; p = (int*)calloc(10, sizeof(int)); if (p == NULL) { printf("%s\n", strerror(errno)); return 0; } for (int i = 0; i < 10; i++) { p[i] = i + 1; } for (int i = 0; i < 10; i++) { printf("%d ", p[i]); } free(p); p = NULL; return 0; }
realloc函数
当我们前面用malloc开辟了一块空间后,我们后面这块空间不够用了,我们想增大空间该怎么做呢?这里就又出现了realloc。realloc调整我们动态开辟的内存大小如果malloc或者calloc开辟的内存后面有足够的空间,那么realloc就是在该内存后面再额外开辟空间,如果rmalloc或calloc后面的空间不够,那么realloc就会重新找一块内存足够大的空间开辟内存,将原来内存中的数据拷贝到新的空间内,并返回新开辟的空间的地址。
void* realloc (void* ptr, size_t size);
realloc函数的参数一个是需要修改的动态内存的地址,一个是无符号整型,单位是字节。
#include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> int main() { int* p = NULL; p = (int*)calloc(5, sizeof(int)); if (p == NULL) { printf("%s\n", strerror(errno)); } for (int i = 0; i < 5; i++) { p[i] = i+1; } int* ptr = (int*)realloc(p, 40); if (ptr != NULL) { p = ptr; } for (int i = 5; i < 10; i++) { p[i] = i + 1; } for (int i = 0; i < 10; i++) { printf("%d ", p[i]); } return 0; }
在知道了如何使用动态内存你函数了之后,我们来看看在使用动态内存函数的时候常见的错误吧。
动态内存函数使用过程中常见错误
1.对NULL指针的引用操作
void test() { int *p = (int *)malloc(INT_MAX/4); *p = 20;//如果p的值是NULL,就会有问题 free(p); }
所以我们在使用动态内存函数开辟空间的时候,最好判断一下是否开辟成功,这样能减少错误。
2.对动态开辟空间的越界访问
void test() { int i = 0; int *p = (int *)malloc(10*sizeof(int)); if(NULL == p) { exit(EXIT_FAILURE); } for(i=0; i<=10; i++) { *(p+i) = i;//当i是10的时候越界访问 } free(p); }
不管是数组还是动态内存开辟的空间,我们在使用时要尤其注意不要越界访问。
3.对非动态开辟内存使用free释放
我们知道free函数只能释放动态开辟的内存,当我们对其他地方的空间使用free函数释放时就会出现错误。
#include<stdio.h> #include<stdlib.h> int main() { int arr[10] = { 0 }; for (int i = 0; i < 10; i++) { arr[i] = i + 1; } for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } free(arr); return 0; }
大家看到没,如果对非动态内存开辟的空间进行free释放,将会出现错误。
4.使用free释放一块动态开辟内存的一部分
void test() { int *p = (int *)malloc(100); p++; free(p);//p不再指向动态内存的起始位置 }
5. 对同一块动态内存多次释放
void test() { int *p = (int *)malloc(100); free(p); free(p);//重复释放 }
6.动态开辟内存忘记释放(内存泄漏)
void test() { int *p = (int *)malloc(100); if(NULL != p) { *p = 20; } } int main() { test(); while(1); }
如果内存泄漏,那么我们电脑的内存会被一直消耗,直到内存被耗尽(但是呢,这种情况一般不会出现,因为计算机也是聪明的,当计算机发现有应用非法占用大量内存的时候将会采取一定的措施来防止内存耗尽)。
所以一定不让忘记释放动态开辟的内存。
总结
那么到这里我今天的动态内存管理的分享就到这里了,感谢大家的观看,后续我也会分享动态版本的通讯录,记得关注哦!!!