自上次发现自己对这几个C函数不熟悉,就打算抽空整理一下,也就现在吧。这几个函数都是跟堆内存打交道的,还有一个好玩的函数--alloca,它是跟栈内存打交道的,我想留在以后研究出好玩点的来,再专门为其写一篇铭文。
1.malloc:
头文件:<stdlib.h>
原型:extern void *malloc(unsigned int num_bytes);
功能:分配长度为num_bytes字节的内存块。如果分配成功则返回指向被分配内存的指针,否则返回NULL。
机理:malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要进行返回值的判断。
2.realloc:
头文件:<stdlib.h>
原型:extern void *realloc(void *mem_address, unsigned int newsize);
功能:改变指针mem_address所指向区域的大小为newsize。如果成功则返回指针mem_address,否则返回NULL。
机理:先判断当前的指针mem_address是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
3.calloc:
头文件:<stdlib.h>
原型:void* calloc(size_t n, size_t size);
功能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
机理:在malloc的基础上加上内存空间清0操作。
4.free:
头文件:<stdlib.h>
原型:void free (void* ptr);
功能:释放指针ptr指向的堆内存。
5.Example
#include <stdlib.h> /* malloc, calloc, realloc, free */ int main () { int * buffer1, * buffer2, * buffer3; buffer1 = (int*) malloc (100*sizeof(int)); buffer2 = (int*) calloc (100,sizeof(int)); buffer3 = (int*) realloc (buffer2,500*sizeof(int)); free (buffer1); free (buffer3); return 0; }
下面是好玩的地方:
1.malloc与calloc的区别:函数malloc不能初始化所分配的内存空间,而函数calloc能。函数calloc() 会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元素分配内存,那么这些元素将保证会被初始化为0;如果你是为指针类型的元素分配内存,那么这些元素通常会被初始化为空指针;如果你为实型数据分配内存,则这些元素会被初始化为浮点型的零.
2.realloc需要注意的安全问题:
(1)从realloc的机理得知,其返回的地址不一定是原地址,这点尤为重要. realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩张或是缩小,原有内存的中内容将保持不变.当然,对于缩小,则被缩小的那一部分的内容会丢失.realloc并不保证调整后的内存空间和原来的内存空间保持同一内存地址.相反,realloc返回的指针很可能指向一个新的地址.
(2)下面说下realloc的扩展:
void *ptr = realloc(ptr, new_size); if (!ptr) { 错误处理 }
这里就引出了一个内存泄露的问题,当realloc分配失败的时候,会返回NULL。但是参数中的ptr的内存是没有被释放的。如果直接将realloc的返回值赋给ptr。那么当申请内存失败时,就会造成ptr原来指向的内存丢失,造成泄露。
正确的处理应该是这样:
void *new_ptr = realloc(ptr, new_size); if (!new_ptr) { 错误处理。 } ptr = new_ptr
<2>
void *new_ptr = realloc(old_ptr, new_size); //其它代码 ...... ......
这种bug由一种不好的编程习惯引发的。即认为申请内存始终可以成功,因此并不检查malloc的返回值。这在一般情况下,不会引发问题。但是对于realloc来说,当new_size为0时,realloc返回NULL。而在后面的代码上,继续使用new_ptr,比如会导致程序crash。