1.为什么使用动态内存分配
当声明数组时,必须用一个编译时常量指定数组的长度,但数组长度往往运行时才知道,这时所需的内存空间取决于输入数据。通常采取声明一个较大的数组的方式,它可以容纳可能出现的最多元素。这种方法优点是简单,缺点为:
1)这种声明在程序中引入了人为的限制,为了尽可能包含所有情况,往往声明大小较大:
2)如果程序实际需要的元素比较少时,巨型数组的绝大部分内存空间都被浪费了;
3)如果程序超过容纳范围时,程序必须以一种合理的方式作出相应;
2.malloc与free
malloc与free用于执行动态内存的分配和释放,共同维护一个可用内存池;
当程序需要一块内存时,调用malloc从内存池中提取一块合适的内存,并向该程序返回一个指向这块内存的指针,这块内存此时并没有以任何方式进行初始化,对内存初始化是非常重要的,1)手动初始化,2)使用calloc函数;当这块内存不在使用时,可以调用free函数将它归还给内存池, 这两个函数声明在头文件stdlib.h中.
malloc若申请内存成功,malloc就返回一个指向被分配的内存块起始位置的指针;
malloc分配的是一块连续的内存,malloc实际分配有可能比申请的稍微多一点,这种行为是由编译器定义的;
malloc若分配所需内存fail,malloc就返回一个NULL指针,因此确保每个malloc返回并非NULL是非常重要的;
malloc返回一个类型为void *的指针,正是如此,标准表示的void *类型的指针可以转换为其它任何类型的指针,但在有些编译器需要注意,可能需要在转换时使用强制类型转换.
对于边界对齐的机器,malloc返回的内存起始位置将始终能够满足对边界对齐要求最严格的类型的要求;
free的参数必须要么是NULL,要么是一个先前malloc、calloc或realloc返回的值,向free传递一个NULL参数不会产生任何效果.
3.calloc和realloc
malloc与calloc之间的主要区别是后者在返回指向内存的指针前把它初始化为0,另一个较小的区别在于它们请求内存数量的方式不同,calloc的参数包括所需元素的数量和每个元素的字节数。
realloc用于修改一个原先已经分配的内存块的大小。使用它可以使一块内存扩大或缩小,如果它用于扩大一块内存,那么这块内存原先的内容依然保留,新增的内存添加到原先内存后面,新内存并未以任何方法进行初始化。若缩小一个内存块,内存块尾部部分内存便被拿掉,剩余部分内存的原先内容依然保留。若原先的内存块无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新的块上,此时使用realloc后,就不能再使用指向旧内存的指针,而是应该改用realloc所返回的新指针。
4.使用动态分配的内存
int *pi;
pi =malloc(25 *sizeof(int));
if(pi==NULL){
printf("out of memory\n");
exit(1);
}
当获取一个指针后,就可以使用间接访问和指针运算来访问数组的不同整数位置。
5.常见的动态内存错误
对NULL指针进行解引用操作、对分配的内存进行操作时越界、释放并非动态分配的内存、试图释放一块动态分配的内存的一部分以及一块动态内存被释放之后被继续使用。
最常见的错误是忘记检查所请求的内存是否成功分配,第二大错误是操作内存存在越界(上下越界都可能)。
内存泄露:当动态分配内存不在需要使用时,它应该被释放回内存池,若申请后不释放将会引起内存泄露,内存泄露将一点一点榨干可用内存,最终使其一无所有。
6.内存分配实例
1)使用普通数组作为缓冲区;
2)字符串:超过缓存区后,可分配一个更大的缓存区,把剩余部分也装到里面;
3)结构体:任一成员分配内存失败,所有已分配内存将被释放,并返回一个NULL指针;
7,总结
1)动态分配内存允许程序为一个长度在运行时在运行时才知道的数组分配内存空间;
2)malloc参数就是所需分配的内存的字节数,calloc的参数是需要分配的元素个数和每个元素的长度,calloc会将申请的内存初始化为0,malloc并不会以任何初始化动作,realloc改变一块动态分配内存的大小。增加内存大小时有可能采取的方法是把原有内存块上的所有复制到一个新的、更大的内存块上。当分配动态内存不在使用时,应该调用free函数将其释放掉,内存在被释放后便不能再被访问。
3)内存分配失败将会返回NULL指针。错误的访问分配内存之外的区域所引起的后果类似越界访问一个数组,但这个错误还可能破坏可以内存池,导致失败,当一个指针不是从早先的malloc/calloc/realloc函数返回的,它是不能作为参数传递给free函数的。
8.警告总结
1)不检查从malloc函数返回的指针是否为NULL;
2)访问动态分配的内存之外的区域;
3)向free函数传递一个并非由malloc函数返回的指针;
4)在动态内存被释放之后再访问它;
9.编程提示总结
1)动态内存分配有助于消除程序内部存在的限制;
2)使用sizeof计算数据类型的长度,提高程序的可移植性;