为什么要使用动态内存管理?
在c语言平时的使用中,我们可能更加习惯于用数组去存放数据。但创建数组时[ ]中只能放一个常量,如果数组长度不够存放数据了,需要手动扩展数组,这样实在过于麻烦,并且在现实的使用中我们一般无法知道多大的数组才够用。这时就需要用到动态内存管理,让程序自动扩容。
malloc函数的使用
malloc函数会在内存的堆区申请一块空间给我们使用。因此我们需要传一个int类型的值给malloc函数,这个值代表我们需要的字节个数。同时malloc函数会返回一个void*类型的指针变量,我们需要将它强制类型转换成我们需要的指针变量。
例如,我想要一个int类型的数组,数组长度为10,就可以如此写
int* pa = (int *)malloc(10 * sizeof(int));//也可以直接写40
之后我们就可以通过指针变量pa来对我们所申请的空间赋值。
当然开辟空间也可能失败,如果失败,malloc函数会返回一个空指针,如果使用不当会导致程序崩溃。因此,我们需要对pa进行判断。
if(NULL == pa){
perror(" ");//报错
return;
}
ps:malloc函数在申请空间时是不会对这块空间进行初始化的,所以不赋值直接打印的话会出来一些奇奇怪怪的东西。
calloc函数的使用
calloc函数与malloc函数大同小异。calloc函数在传参时将数据个数与数据所占字节分开来了,因此如果想要用calloc函数达到以上效果的话要这么写
int* pa = (int*)calloc(10, sizeof(int));
ps:calloc函数在申请空间时会将这块空间全部初始化为0,因此如果需要在创建数组时初始化的,推荐使用calloc。
realloc函数的使用
看了上面的两个函数后,是不是感觉用malloc函数和calloc函数创建的数组的大小仍然是固定的?
这就要看动态内存管理的关键---realloc函数了。
realloc函数可以对我们之前用malloc函数或calloc函数申请的空间进行扩大或缩小。realloc函数需要两个参数,第一个是之前指向空间的指针变量(注:这个指针变量只能指向空间的首地址,如果传入其他指针,如数组第2个元素的地址,就会报错,因此,之前用来接收的指针变量不能轻易修改)。第二个变量是修改后的空间大小。realloc函数返回的也是void*类型的变量,需要进行强制类型转换。
可能会有人有疑问,为什么realloc函数接收还要返回值,直接使用原来的指针变量不就行了。
实际上,realloc修改完空间大小后,数据可能就不再原来的那一块空间上了。举个例子来说,我同时开辟了数组a[]和数组b[]两块空间,现在我要对a进行扩容,但如果正常向后扩容会和数组b所在的空间冲突,这时候realloc函数就会在堆区重新找一块大小合适的空间,并返回这块空间的首地址。所以我们需要对指向这块空间的指针变量进行重新赋值。
当然,只是如此还不够严谨。realloc函数修改空间大小当然也会失败返回空指针,如果直接付给指针变量的话,我们就找不到这块空间了,因此我们需要创建一个临时变量来存储返回的地址,如果不是空指针,就把它付给指针变量。
以上面的例子来说,如果我想要将数组扩容为20个
int* tmp = (int*)realloc(pa, 20 * sizeodf(int));
if(NULL = tmp){
perror(" ");
return;
}
pa = tmp;
ps:realloc函数第一个参数可以传空指针,如果这么做,realloc函数作用与malloc函数相同。
free函数的使用
free函数是用来释放我们所申请的空间的,如果将malloc和calloc看作是借的话,那free就是还。
我们只需将空间的首地址传给free函数即可完成释放。
free(pa);
当然,如果只是释放,那我们依旧能够通过pa来找到这块空间,这是十分危险的,因此我们需要将pa设置成空指针。
pa = NULL;
在我们使用完空间后一定要记得释放空间,否则就会造成内存泄漏。
ps:1.free函数可以传空指针,如果传空指针,free函数就什么都不做。
2.free函数只能传由malloc和calloc开辟出的空间的首地址,传其他地址会报错。
什么是内存泄漏
内存泄漏就像借书不还,如果我们一直不将空间还给系统的话,系统内存就会越来越少。虽然程序结束后,申请的内存会自动还给系统,但如果是一天24小时工作的程序就不行了。因此,我们要养成用完就还的习惯。