一、原型:extern void *malloc(unsigned int num_bytes);
头文件:#include <malloc.h> 或 #include <alloc.h> (注意:alloc.h 与 malloc.h 的内容是完全一致的。)
功能:分配长度为num_bytes字节的内存块
说明:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
当内存不再使用时,应使用free()函数将内存块释放。
举例:
- #include<stdio.h>
- #include<malloc.h>
- int main()
- {
- char *p;
- p=(char *)malloc(100);
- if(p)
- printf("Memory Allocated at: %x/n",p);
- else
- printf("Not Enough Memory!/n");
- free(p);
- return 0;
- }
二、函数声明(函数原型):
void *malloc(int size);
说明:malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。这个在MSDN上可以找到相关的解释,具体内容如下:
malloc returns a void pointer to the allocated space, or NULL if there is insufficient memory available. To return a pointer to a type other than void, use a type cast on the return value. The storage space pointed to by the return value is guaranteed to be suitably aligned for storage of any type of object. If size is 0, malloc allocates a zero-length item in the heap and returns a valid pointer to that item. Always check the return from malloc, even if the amount of memory requested is small.
三、malloc与new的不同点
从函数声明上可以看出。malloc 和 new 至少有两个不同: new 返回指定类型的指针,并且可以自动计算所需要大小。比如:
int *p;
p = new int; //返回类型为int* 类型(整数型指针),分配大小为 sizeof(int);
或:
int* parr;
parr = new int [100]; //返回类型为 int* 类型(整数型指针),分配大小为 sizeof(int) * 100;
而 malloc 则必须由我们计算要字节数,并且在返回后强行转换为实际类型的指针。
int* p;
p = (int *) malloc (sizeof(int));
第一、malloc 函数返回的是 void * 类型,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,报错:“不能将 void* 赋值给 int * 类型变量”。所以必须通过 (int *) 来将强制转换。
第二、函数的实参为 sizeof(int) ,用于指明一个整型数据需要的大小。如果你写成:
int* p = (int *) malloc (1);
代码也能通过编译,但事实上只分配了1个字节大小的内存空间,当你往里头存入一个整数,就会有3个字节无家可归,而直接“住进邻居家”!造成的结果是后面的内存中原有数据内容全部被清空。
malloc 也可以达到 new [] 的效果,申请出一段连续的内存,方法无非是指定你所需要内存大小。
比如想分配100个int类型的空间:
int* p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100个整数的内存空间。
另外有一点不能直接看出的区别是,malloc 只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的。
除了分配及最后释放的方法不一样以外,通过malloc或new得到指针,在其它操作上保持一致。
总结:
malloc()函数其实就在内存中找一片指定大小的空间,然后将这个空间的首地址范围给一个指针变量,这里的指针变量可以是一个单独的指针,也可以是一个数组的首地址,这要看malloc()函数中参数size的具体内容。我们这里malloc分配的内存空间在逻辑上连续的,而在物理上可以连续也可以不连续。对于我们程序员来说,我们关注的是逻辑上的连续,因为操作系统会帮我们安排内存分配,所以我们使用起来就可以当做是连续的。
c语言中的动态内存分配函数malloc和free使用起来很灵活,但是也很容易导致错误,
如果知道了malloc和free的实现原理,那么,出错的机会就很小了。
malloc的实现原理:
操作系统维护了一个将可用的内存块连接为一个长长的列表的所谓空闲链表。
调用malloc函数时,操作系统沿链表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到链表上。调用free函数时,它将用户释放的内存块连接到空闲链表上。
free的实现原理:
操作系统在调用malloc函数时,会默认在malloc分配的物理内存前面分配一个数据结构,这个数据结构记录了这次分配内存的大小,在用户眼中这个操作是透明的。
那么当用户需要free时,free函数会把指针退回到这个结构体中,找到该内存的大小,这样就可以正确的释放内存了。
通过这种内存分配机制,可以解释很多c语言中的迷惑问题:
1)用户已经free了一段内存,为什么还可以调用该指针,并且编译器不报错?
因为,free函数的作用只是告诉操作系统该内存不用了,可以收回,操作系统就把该内存链接到链接表上,
但是这段内存用户还是可以访问到的,只是该内存的值可能已经发生了变化,这种情况也叫作野指针。
解决办法就是,在free后,把该指针指向NULL。
- 12 void Test(void)
- 13 {
- 14 char *str = (char *) malloc(100);
- 15
- 16 strcpy(str,"hell");
- 17
- 18 free(str);
- 19 printf("str1=%s\n",str);
- 20 if(str != NULL)
- 21 {
- 22 strcpy(str,"world");
- 23 printf("str=%s\n",str);
- 24 }
- 25 }
会打印出:
str1=
str=world
free 只是释放的str指向的内存空间,它本身的值还是存在的.
所以free之后,有一个好的习惯就是将str=NULL.
此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,
尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world来。
这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。
当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的,只不过。。。。。。。。最好别这么干。
2)用户在malloc时,分配了100字节,int *指针指向该内存,那么free时,是释放100字节还是释放4字节?
因为操作系统在malloc分配内存时,会在该内存的物理地址的前面维护一个数据结构,该数据结构记录了这次分配内存的大小,所以当用户free时,free函数会把该int *指针退回到该数据结构中,找到该内存的大小,这样free函数就可以正确的释放
该段内存了。
答案:释放100字节,而不是4字节。
3)用户malloc了100字节的内存,int *指针指向该内存,同时定义了char *的指针,也指向该内存,那么free(char *),是释放
1个字节还是释放100个字节?
原理同上面的问题一样,只是这个问题的更具隐蔽性,也容易误解。
因为free函数的原理是退回到该物理内存的前面,找到记录该内存大小的数据结构,所以,即使是char *指针,free也可以正确的释放该内存。
答案:释放100字节,而不是1个字节
free需要释放malloc定义的首地址。
eg:
int *a;
a = (int *)malloc(100);
*a++ = 0;
*a++ = 1;
再用free释放的的时候一定把a的地址指向malloc分配时候的首地址
a--;
a--;
这样采用用free(a);
当然也可以定义一个int *b;在最开始的时候 b = a;
这样就可以free(b)了。