1、 系统、开发工具环境:
所有测都是使用的vs2010版本。操作系统为windows xp。
2、 概况
在C语言中,可以分配内空间的函数有malloc、calloc、realloc。释放函数free。关于C++里面的new和delete,他们两个跟c很大不同。New和delete是C++运算符。New建立的是一个对象,对象要求对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。Malloc和free是办不到的。但是new和delete各自都调用了malloc和free。所以重还是malloc和free。
要使用malloc函数,可以用stdlib.h或者malloc.h。
Malloc在malloc.h定义如下:
void * __cdecl malloc(_In_ size_t _Size);
我们比较熟悉的就是
void *malloc(unsigned int num_bytes);
num_bytes是指示的要分配的空间的大小。返回值是void类型。所以我们要强制性转换成需要的类型。
如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL
3、 malloc free基本思想
malloc函数有一个将可用的内存块连接为一个长长的列表的所谓堆空闲链表(或者笛卡尔树或者内存桶)。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到空闲链表合适的位置上。当没有足够大的内存时候,就要将小的内存的合并成一个大的内存(合并的时候一定要是几个内存块相邻,不然怎么合并?)
Free跟malloc相反,当free掉一个空间之后,就会将相应的内存放回到链表合适的位置上。
通过空闲链表,我们可以很轻松进行管理。
Malloc分配的内存都在堆上。
malloc分配结构
比如下面一段代码:
void main()
{
char*p=(char *)malloc(sizeof(char) * 10);
memcpy(p,"Ilove you1",11);
printf("%s\n",p);
free(p);
}
给p分配了10个大小。Free只有一个函数,那么free的时候,怎么知道该怎么回收内存呢?根据的只是一个p指针吗?还有,malloc有越界检查吗?一切看起来都感觉只是一个10个大小的空间,怎么能胜任这么多要求。
在有的free源码里面,有这么一段话:
void free(void *ptr){
structmem_control_block *free;
free = ptr - sizeof(struct mem_control_block);
free->is_available = 1;
return;
}
Free居然是ptr减去了一个struct结构的大小,然后设置了一个标志。我们不得不有理由相信,malloc分配空间的时候,一定还分配了一些空间来当做控制信息,或者说是一个header。实际情况的确如此。可以简单概括成32+size+4的结构(目前测试是这个结构,可能各个系统不一样。)
我们就按照第一段代码调试free,看看具体情况。
设置断点,先看看分配的空间,到free之后,F11进行调试,查看内存。
p指向的内存空间是cd,没有初始化。注意后面的4个fd。
当memcpy之后,p内存值变化了。
Free时候F11调试,进去,到memeset这个位置,内存还是好好的,注意看上面有一段话:
fill theentire block including header with dead-land-fill
这说明,的确,malloc分配的时候,有一个头。接着下一步。
椭圆的是我们p内存的位置,看矩形,说明memset的是这个矩形红色的内容。数了一下,前面32个字节,中间10个字节,后面4个字节。
这足以说明,分配空间是有一个头,有一个尾部。尾部4个字节是用来free的时候检查,看是否越界。比如,你分配了10个空间,但是你memcpy了11个,这个时候free就会出问题。
这里给出malloc分配的头部结构:
typedef struct_CrtMemBlockHeader
{ // Pointer tothe block allocated just before this one: struct _CrtMemBlockHeader*pBlockHeaderNext;
// Pointer to the block allocated just afterthis one:
struct_CrtMemBlockHeader *pBlockHeaderPrev;
char *szFileName; // File name
int nLine; //Line number
size_t nDataSize;// Size of user block
int nBlockUse; //Type of block
long lRequest; //Allocation number
// Buffer justbefore (lower than) the user's memory:
unsigned chargap[nNoMansLandSize];
}_CrtMemBlockHeader;
/* In an actual memory block in the debugheap,
* this structure is followed by:
* unsigned chardata[nDataSize];
* unsigned charanotherGap[nNoMansLandSize];
*/
所以free是根据这个头部来决定怎么free的。
4、 realloc
这个函数是来增加大小的,如果该存储区后有足够的空间可供扩充,则可在原存储区位置上向高地址方向扩充,并返回传送给它的同样的指针值。(这样情况是最好的)如果原存储区后没有足够的空间,则realloc分配另一个足够大的存储区,将现存内容复制到新分配的存储区。若新内存地址原来一样,就会free掉原来的地址,否则,不会。
Realloc原型:
void *realloc(void *mem_address, unsignedint newsize);
newsize 是新的大小。Newsize要求要大于原来的大小,但是如果小于的话,也没有关系,但是可能会丢失数据。
例子如下:
void main()
{
char*p=(char *)malloc(sizeof(char) * 10);
memcpy(p,"Ilove you",10);
p = (char*)realloc(p,sizeof(char)*90);
printf("%s\n",p);
free(p);
}
注意p的地址,
同样的地址已经变成随意内容,说明realloc重新分配了空间,原空间已经free了。查看p的内存,如下:
注意,realloc之后我们还是用的p,如果换成其他的比如q指针,p指针是不会消除的,还是存在着。除非主动free。
关于realloc总结如下:
1. realloc失败的时候,返回NULL ,realloc之后一定要检查是否分配成功
2. realloc失败的时候,原来的内存不改变,不会释放也不会移动
3. 假如原来的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉,realloc返回新内存的地址 (前提是指针是同一个)
4. 如果size为0,效果等同于free()。这里需要注意的是只对指针本身进行释放,例如对二维指针**a,对a调用realloc时只会释放一维,使用时谨防内存泄露。
5. 传递给realloc的指针必须是先前通过malloc(), calloc(), 或realloc()分配的
6.传递给realloc的指针可以为空,等同于malloc。
7、新分配的空间一定要大于原来的空间。小于源大小的话,相当于就缩小了空间,也是可以分配的。但是数据的话就可能丢失。
5、 calloc
函数原型:
void *calloc(size_t nobj, size_t size)
该函数相当于malloc+memset
Malloc calloc 都是在heap上分配空间。最大的区别就是calloc是会初始化内存空间的,malloc不会,需要自己调用memset去初始化。次要区别可以从参数看的出来,calloc返回的某种对象组成的数组。所以一些程序员在给数组分配空间的时候,愿意使用calloc。但是其实这两个区别不打了,malloc申请空间也可以当成数组使用。