名称
malloc,free,calloc,realloc--分配和释放动态内存
概要
#include <stdlib.h>
void *malloc(size_tsize);
void free(void *ptr);
void *realloc(void*ptr, size_t size);
void *calloc(size_tnmemb, size_tsize);
描述
malloc()函数分配size个字节的内存并返回指向已分配内存的指针。该内存未初始化。如果size为0,malloc()或者返回空指针NULL,或者返回能成功传给free()函数的唯一的指针值。
free()函数释放指针ptr指向的内存空间,ptr必须是调用malloc()、calloc()或realloc()时返回的指针值,否则,或者如果free(ptr)已经被调用过了,会发生不确定的行为。如果ptr是空指针NULL,则不会执行任何操作。
calloc()函数为一个nmemb个元素大小为size个字节的数组分配内存并返回指向已分配内存的指针。已分配内存的内容被清零。如果nmemb或size为0,calloc()或者返回空指针NULL,或者返回能成功传给free()函数的唯一的指针值。
realloc()函数改变ptr指向的内存块的大小为size个字节。从内存块的起始地址到新旧大小最小值之间区域的内容不变。如果新的内存块大于原来的内存块,新增加的内存未被初始化。如果ptr是空指针NULL,对于所有size来说,该调用就等效于malloc(size);如果size为0,并且ptr是非空指针NULL,则该调用等效于free(ptr)。只要ptr是非空指针NULL,则必须是之前调用malloc()、calloc()或realloc()时返回的指针值。如果指向的区域移动了,会进行free(ptr)操作。
返回值
malloc()函数和calloc()函数返回指向已分配区域的指针。该内存对于任何类型的变量都已经合理地对齐。出错时,返回空指针NULL。空指针也可能在成功调用当size为0的malloc()函数时返回,或成功调用nememb为0或size为0的calloc()函数时返回。
free()函数没有返回值。
realloc()函数返回指向新分配内存区的指针,该内存区已对于任何类型的变量已合理对齐,该指针可能与ptr不同,或者当请求失败时返回空指针NULL。如果size为0,或者返回空指针NULL,或者返回要传给free()函数的合适的指针。如果realloc()函数失败,原来的内存块不受影响,既未移动,也未释放。
注释
通常,malloc()函数自堆中分配内存,并使用sbrk根据需求调整堆的大小。当正在分配的内存块大于MMAP_THRESHOLD字节数时,glibc的malloc()实现使用mmap将内存的分配当作私有的匿名映射。MMAP_THRESHOLD缺省是128KB,但可以使用mallopt调整大小。使用mmap进行的内存分配不受RLIMIT_DATA资源限制的影响。
UNIX 98标准要求malloc()、calloc()和realloc()在出错时设定errno为ENOMEM。Glibc假定这些要求都已经实现。如果用户使用了没有设定errno的私有的malloc实现,某些特定的库函数在errno上可能会毫无理由地失败。
英文网址:http://www.kernel.org/doc/man-pages/online/pages/man3/free.3.html
使用注意事项
1. 函数使用中需要注意:
A、申请了内存空间后,必须检查是否分配成功。
B、当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止后面程序不小心使用了它。
C、这两个函数应该是配对的。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。只能释放一次,如果释放两次及两次以上会
出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。
D、虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一
些编译器的检查。
2. malloc()从堆得到内存空间。
堆是大家共有的空间(分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间)。堆在操作系统对进程 初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
与之对应的是栈。栈是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立。每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
通过上述内容,malloc()和free()只会涉及到堆的使用,可由用户控制;(为防止内存泄露,malloc()使用结束一定要记得释放!!)
而栈的使用是由系统自动控制的,无需用户介入。
3. malloc()和free()的机制
free()函数非常简单,只有一个参数,只要把指向申请空间的指针传递给free()中的参数就可以完成释放工作。
这里要追踪到malloc()的申请问题了。申请的时候实际上占用的内存要比申请的大。因为超出的空间是用来记录对这块内存的管理信息。
malloc()申请的空间实际我觉得就是分了两个不同性质的空间。一个就是用来记录管理信息的空间,另外一个就是可用空间了。而用来记录管理信息的实际上是一个结构体。
在C语言中,用结构体来记录同一个对象的不同信息是天经地义的事!下面看看这个结构体的原型:
struct mem_control_block {
int is_available; //这是一个标记?
int size; //这是实际空间的大小
};
对于size,这个是实际分配空间大小。
下面看看free()的源代码。
程序代码:
// code... void free(void *ptr) { struct mem_control_block *free; free = ptr - sizeof(struct mem_control_block); free->is_available = 1; return; }
看一下函数第二句,这句非常重要和关键。其实这句就是把指向可用空间的指针倒回去,让它指向管理信息的那块空间,因为这里是在值上减去了一个结构体的大小!
图示如下:
该文作者原先有错误的认识,就是认为指向那块内存的指针不管移到那块内存中的哪个位置都可以释放那块内存!
但是,这是大错特错!释放是不可以释放一部分的!首先这点应该要明白。而且,从free()的源代码看,ptr只能指向可用空间的首地址,不然,减去结构体大小之后一定不是指向管理信息空间的首地址。所以,要确保指针指向可用空间的首地址!
该错误认识通过上图更能清晰地看出来,只有当ptr指向可用空间的首地址时,在ptr减去struct mem_control_block的大小后才能使得后面的语句free->is_available得到有效数值。否则,传给系统的数值就肯定不是正确的,由此造成的后果也不可预测了。
4. 错误使用举例。
int main(int argc, char *argv[]) { char *buffer = NULL, *str = NULL; char *a = "abcdefghij"; int length = 10, i = 0; buffer = (char *)malloc(length * sizeof(char)); if(!buffer) { printf("1:malloc error!\n"); return 0; } memset(buffer, 0, length); for(i = 0; i < length - 1; i++) { *buffer++ = a[i]; } fprintf(stderr, "buffer: %s\n", buffer); free(buffer); str=(char *)malloc(length * sizeof(char)); if(!str) { printf("2:malloc error!\n"); return 0; } memset(str, 0, length); for(i = 0; i < length - 1; i++) { str[i] = a[i]; } fprintf(stderr, "str: %s\n", str); free(str); }
上面程序中同样使用malloc()函数为buffer和str分配了10个字节的内存,最后都通过free()的方式释放内存。其中,malloc(str)和free(str)都是正确的,而malloc(buffer)和free(buffer)就错了。因为指针buffer的值发生改变,已经不再是通过malloc(buffer)返回的指针值了。
总之,一句话,为防止内存泄露,malloc()使用结束一定要记得用free(ptr)释放!!
并且,ptr一定要是malloc()分配时得到的指针地址。
malloc()和free()机制等部分转载了bccn作者ID:lj_860603(键键)的文章,如下:
http://www.bccn.net/Article/kfyy/cyy/jszl/200608/4238_2.html