一、malloc()和free()的基本概念以及基本用法:
1、函数原型及说明:
void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。
关于分配失败的原因,应该有多种,比如说空间不足就是一种。
void free(void *FirstByte): 该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。
2、函数的用法:
其实这两个函数用起来倒不是很难,也就是malloc()之后觉得用够了就甩了它把它给free()了,举个简单例子:
程序代码:
// Code...
char *Ptr = NULL;
Ptr = (char *)malloc(100 * sizeof(char));
if (NULL == Ptr)
{
exit (1);
}
gets(Ptr);
// code...
free(Ptr);
Ptr = NULL;
// code...
就是这样!当然,具体情况要具体分析以及具体解决。比如说,你定义了一个指针,在一个函数里申请了一块内存然后通过函数返回传递给这个指针,那么也许释放这块内存这项工作就应该留给其他函数了。
3、关于函数使用需要注意的一些地方:
A、申请了内存空间后,必须检查是否分配成功。
B、当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。
C、这两个函数应该是配对。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会
出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。
D、虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一
些编译器的检查。
好了!最基础的东西大概这么说!现在进入第二部分:
二、malloc()到底从哪里得来了内存空间:
1、malloc()到底从哪里得到了内存空间?答案是从堆里面获得空间。也就是说函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。就是这样!
说到这里,不得不另外插入一个小话题,相信大家也知道是什么话题了。什么是堆?说到堆,又忍不住说到了栈!什么是栈?下面就另外开个小部分专门而又简单地说一下这个题外话:
2、什么是堆:堆是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程 初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
什么是栈:栈是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立。每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
以上的概念描述是标准的描述,不过有个别语句被我删除,不知道因为这样而变得不标准了^_^.
通过上面对概念的描述,可以知道:
栈是由编译器自动分配释放,存放函数的参数值、局部变量的值等。操作方式类似于数据结构中的栈。
堆一般由程序员分配释放,若不释放,程序结束时可能由OS回收。注意这里说是可能,并非一定。所以我想再强调一次,记得要释放!
----------------------------------------------------------------------------
(转)
malloc函数和free函数
假设您的程序在执行过程中需要分配一定量的内存。您可以随时调用malloc函数从堆中申请一块内存。在操作系统为您的程序预留出这块内存,之后您就可以随意使用它了。用完之后,要使用free函数将这块内存返回给操作系统进行回收。以后其他程序还可以按自己的需要预留这块内存。
作为例子,下面的代码演示了最简单的使用堆的方法:
int main()
{int *p;p = (int *)malloc(sizeof(int));if (p==0)
{printf("错误:内存不足n");return 1;
}*p=5;printf("&dn", *p);free(p);return 0;}
程序的开始调用了malloc函数,这个函数做了三件事:
- malloc语句首先检查堆上的空闲内存总数,然后判断:“有没有足够的空闲内存可以分配一个所申请的大小的内存块呢?”申请的内存块大小是由传入malloc的参数确定的——本例中的sizeof(int)是4个字节。若内存不足,malloc函数会返回零地址告知发生的错误(零地址的另一种表示是NULL,它在C代码中很常用)。否则malloc函数继续执行。
- 若堆上有足够的内存,系统就从堆上“分配”或“预留”出指定大小的内存块。预留的目的是为了防止多个malloc语句恰巧使用同一个内存块。
- 接下来系统将预留出的内存块的地址保存到指针变量中(本例中就是p)。指针变量本身保存了一个地址。被分配的内存块能够存储一个指定类型的数值,而指针正是指向此数值。
下图显示了调用malloc之后的内存状态:
右边的方框表示malloc分配的内存块。
接着程序用if (p==0)检查指针p以确定分配申请成功(此行也可写成if (p==NULL)甚至if (!p))。如果分配失败(p等于零),则程序终止,否则程序将分配的内存块初始化为5,然后打印内存块的值,接着调用free函数将内存块返还给堆,最后退出。
前面的章节有一段代码是将p赋值为一个现成整数i的地址,而本例中的代码和那段代码实际上并无不同。区别只是在于:对于变量i的内存,它是程序预分配内存空间的一部分,有两个名字i和*p;而对于从堆上分配的内存,它只有一个名字*p,且是在程序运行中分配的。两个常见的问题是:
- 每次分配内存后都要检查指针的值是否为零,这真的很重要吗?是的。因为堆的大小取决于当前正在运行哪些程序、它们分配了多少内存等诸多因素,所以一直都在变化,不能保证调用malloc总是成功的。每次调用malloc以后,您都应该检查一下指针以确保其有效性。
- 如果程序结束前我忘记了释放分配的内存块会怎样呢?程序结束以后,操作系统会做“善后处理”:释放可执行代码、栈、全局变量和所有从堆上分配的内存空间以供回收利用。因此,对分配的内存置之不理,在程序结束以后是不会对系统造成持续影响的。但是这种做法会被认为是“不良的风格”,且程序运行中的“内存泄漏”是有害的。这一点下文还会讲到。
下面两段程序显示了两种不同的使用指针的正确方法,旨在区分指针和指针的值在使用上的区别:
void main()
{int*p, *q;p=(int *)malloc(sizeof(int));q=p;*p=10;printf("%dn", *q);*q=20;
printf("%dn", *q);}
此程序的最后输出结果是代码第4行打印的10和代码第6行打印的20。下面是一个内存状态示意图:
下面这个程序稍有不同:
void main()
{int *p, *q;p=(int *)malloc(sizeof(int));q=(int *)malloc(sizeof(int));
*p=10;*q=20;*p=*q;printf("%dn", *p);}
此程序的最后输出结果是代码第6行打印的20。下面是它的内存状态示意图:
注意,编译器会接受*p=*q,因为*p和*q都是整数。这条语句的意思是说:“将q指向的整数传送到p指向的整数中去。”被传送的是数值。编译器也会接受p=q,因为p和q都是指针且指向相同的类型(若s为指向字符的指针则p=s是不允许的,因为它们指向不同类型)。p=q这条语句的意思是说:“将p指向和q相同的内存位置。”换句话说,q指向的地址被传送到了p,因此两个指针指向相同的地址。被传送的是地址。
从这些例子可以知道,初始化指针的方式有四种。在程序中声明一个指针时(如int *p),它开始处于未初始化状态。它可能指向任何位置,因此对它的解引用(取出指针指向的地址中的内容)是错误的。初始化指针就是将其指向一个已知的内存地址。
- 第一种方式是例子中使用的malloc语句。此语句从堆上分配一块内存并将指针指向它。这样指针便完成了初始化,因为它现在保存了一个有效地址,即新分配的内存块的地址。
- 第二种方式,也是刚才用到的,是用p=q这样的语句使p指向和q相同的位置。若q已经指向有效地址,则p完成初始化。指针p将保存q已经保存的有效地址。但若q未初始化或无效,则这个无价值的地址也会传给 p。
- 第三种方式是将指针指向已知地址,如一个全局变量的地址。例如,若i是一个整数且p是一个整型指针,则语句p=&i初始化p为指向i。
- 第四种方式是将指针初始化为零。在使用指针时零是一个特殊值,如下所示:
p=0;
或:
p=NULL;
此语句完成的操作是将零赋给p。指针p指向的地址为零。一般用下面的示意图表示这种情况:
任何指针都可设为指向零地址。虽然p指向零地址,但是它却不指向任何真正的内存块。此指针保存的零值只是一个标志。可以像下面语句这样使用它:
if (p==0)
{...}
或:
while (p!=0){...}
系统会识别零值,如果您无意中解引用一个零指针,系统会报错。例如下列代码:
p=0;*p=5;
程序一般会崩溃。指针p不指向内存块而是零地址,所以不能为*p赋值。后面我们讲到链表时,零指针将被作为一个标志使用。
malloc命令用于分配一个内存块。当此内存块不再需要时还可以将其释放。释放的内存块可以被后来的malloc语句重新分配,这样系统就可以回收内存。释放内存的命令叫做free,它接受一个指针作为参数。free命令完成两件事情:
- 不再预留指针指向的内存块,而是将其返还到堆上的空闲内存区。此内存块可以被随后的语句重新使用。
- 指针被置为未初始化的状态,再次使用前必须重新初始化。
free语句只是将指针还原为未初始化状态并使内存块在堆上重新变成可用状态。
下例显示了如何使用堆。它分配了一块整数内存,写入数据然后输出,最后废除此内存块:
#include
int main()
{int *p;p=(int *)malloc (sizeof(int));*p=10;printf("%dn",*p);free(p);return 0;}
此代码其实只适用于在C中演示分配、使用和释放内存块的过程。malloc用于分配一块指定大小的内存,本例中是sizeof(int)字节(4字节)。C语言的sizeof命令以字节为单位返回任何类型的大小。代码中完全可以写成malloc(4),因为在大部分机器上sizeof(int)等于4个字节。但是使用sizeof可以大大增强代码的可移植性和可读性。
malloc函数返回一个指向被分配内存块的指针。这是一个通用指针,若不经类型转换即使用一般会导致编译器发出类型警告。类型转换(int *)将malloc返回的通用指针转换为一个“指向整数的指针”,即与p一致。C中的free语句将内存块返还给堆以供重新使用。
第二个例子说明的函数和前一例相同,但是用结构体代替了整数。C代码如下:
#include
struct rec
{int i;float f;char c;};
int main()
{struct rec *p;p=(struct rec *) malloc (sizeof(struct rec));(*p).i=10;
(*p).f=3.14;
(*p).c='a';
printf("%d %f %cn",(*p).i,(*p).f,(*p).c);
free(p);
return 0;}
请注意这行:
(*p).i=10;
很多人不明白为什么不能写成:
*p.i=10;
答案是这和C语言的操作符优先级有关。5+3*4的结果是17,不是32,因为在大多数计算机语言中*比+有更高的优先级。C语言中,操作符.比*有更高的优先级,所以要使用括号保持正确的操作顺序。
但是大部分人觉得总是输入(*p).i太麻烦了,因此C提供了一种简洁记法。下面的两条语句完全等效,而第二条的输入更加简便:
(*p).i=10;
p->i=10;
阅读别人代码的时候,您会发现第二种记法比第一种更常用。