指针高级用法详解
数组的元素存储于内存中连续的位置上。当一个数组被声明时,它所需要的内存在编译时就被分配。但是,你也可以使用动态内存分配在运行时为它分配内存。
为什么使用动态内存分配
当你声明数组时,你必须用二个编译时常量指定数组的长度。但是,数组的长度常常在运行时才知道,这是由于它所需要的内存空间取决于输入数据。
malloc和free
。当一个程序另外需要一些内存时,它就调用 malloc 函数, malloc 从内存池中提取一块合适的内存,并向该程序,返回一个指向这块内存的指针。这块内存此时并没有以任何方式进行初始化。如果对这块内存进行初始化非常重要,你要么自己动手对它进行初始化,要么使用 calloc函数。。当一块以前分配的内存不再使用时,程序调用 free 函数把它归还给内存池供以后之需。
c
void *malloc(size_t size);
void *free(void *pointer);
malloc 所分配的是一块连续的内存。对每个从 malloc 返回的指针都进行检查,确保它并非 NULL 是非常重要的。
malloc 又是如何知道你所请求的内存需要存储的是整数、浮点值、结构还是数组呢?它并不知情一-tmnalloc 返回一个类型为 void *的指针,正是缘于这个原因。标准表示一个 void *类型的指针可以转换为其他任何类型的指针。
calloc和realloc
另外还有两个内存分配函数。
c
void *calloc(size_t num_lelments, size_t element_size);
void realloc(void *ptr, size_t new_size);
malloc calloc 之间的主要区别是后者在返回指向内存的指针之前把它初始化为 。这个初始化常常能带来方便,但如果你的程序只是想把一些值存储到数组中,那么这个初始化过程纯属浪费时间。 calloc malloc 之间另一个较小的区别是它们请求内存数量的方式不同。 calloc 的参数包括所需元素的数量和每个元素的字节数。根据这些值,它能够计算出总共需要分配的内存。
realloc 函数用于修改一个原先已经分配的内存块的大小。使用这个函数,你可以使一块内存扩大或缩小。如果它用于扩大一个内存块,那么这块内存原先的内容依然保留,新增加的内存添加到原先内存块的后面,新内存并未以任何方法进行初始化。如果它用于缩小一个内存块,该内存块尾部的部分内存便被拿掉,剩余部分内存的原先内容依然保留。
如果原先的内存块无法改变大小, realloc 将分配另一块正确大小的内存,并把原先那块内存的内容复制到新的块上。因此,在使用 realloc 之后,你就不能再使用指向旧内存的指针,而是应该改realloc 所返回的新指针。
常见的动态内存错误
动态内存分自己最常见的错误就是忘记检查所请求的内存是否成功分配。
动态内存分面己的第二大错误来源是操作内存时超出了分自己内存的边界。
当一个使用动态内存分配的程序失败时,人们很容易把问题的责任推给 malloc free 函数。但它们实际上很少是罪魁祸首。事实上,问题几乎总是出在你自己的程序中,而且常常是由于访问了分配内存以外的区域而引起的。
当你使用 free 时,可能出现各种不同的错误。传递给 free 的指针必须是一个从 mall町、 callocrealloc 函数返回的指针。传给 free 函数一个指针,让它释放一块并非动态分配的内存可能导致程序立即终止或在晚些时候终止。试图释放一块动态分自己内存的一部分也有可能引起类似的问题。
c
pi = malloc(10*sizeof(int));
free(pi+5); // error
释放一块内存的一部分是不允许的。动态分自己的内存必须整块一起释放。但是, realloc 函数可以缩小一块动态分配的内存,有效地释放它尾部的部分内存。
最后,你必须小心在意,不要访问已经被 free 函数释放了的内存。
当动态分配的内存不再需要使用时,它应该被释放,这样它以后可以被重新分配使用。分配内存但在使用完毕后不释放将引起内存泄漏(memory leak) 。在那些所有执行程序共享一个通用内存池的操作系统中,内存泄漏将一点点地榨干可用内存,最终使其一无所有。要摆脱这个困境,只有重启系统。