和在使用一个数据以前必需要对数据进行初始化同样,不然可能会使得数据的值不肯定,那就会给程序埋下很大的隐患,在使用指针以前也必需要对指针进行”初始化“,参见下面的例程1:编程
#include<stdio.h> int main(void) { int *x; *x = 3; return 0; }
这样的代码可能会出现段错误,由于x指针不知道会指向哪一块内存,使用*x=3来更改那块内存的数据有可能访问到非法内存致使段错误,固然也有可能由于没访问到非法内存而没有产生段错误,可是一个健壮的程序不容许存在这样的隐患。segmentfault
再看下面的一个例程2:测试
#include<stdio.h> #include<stdlib.h> int main(void) { int *x; x = (int *)malloc(sizeof(int)); //上面一行代码至关于对指针的初始化,使得指针指向一个合法的内存区域 //准确的表述应该是,使用malloc动态分配一块sizeof(int)大小的内存空间,而后让x指向这块内存 *x = 3; //上面这行代码的方式就不会有访问非法内存的可能,就不会产生段错误 free(x); return 0; }
经过指针释放内存后别忘了将指针置为NULL,或者将这个指针指向另外一个合法的内存地址,总之不能再有指针指向被释放过了的非法的内存空间。网站
上面的例程2中直接调用free来释放内存,由于这是一个很简短的程序因此运行的效果会正如你但愿的那样,可是假如在一个比较大型的项目中,这样的写法就存在着隐患,更为稳定的程序应该这样写,见例程3:线程
#include<stdio.h> #include<stdlib.h> int main(void) { int *x; x = (int *)malloc(sizeof(int)); //上面一行代码至关于对指针的初始化,使得指针指向一个合法的内存区域 *x = 3; //上面这行代码的方式就不会有访问非法内存的可能,就不会产生段错误 free(x); x = NULL; return 0; }
有人会说这不是画蛇添足吗,确实在这个例程里面是画蛇添足,由于这个程序太过简单,free(x) 以后,程序就运行结束了,不会存在再经过x访问被释放了的内存的非法访问内存的问题。指针
可是假如在一个比较大的程序中,在程序的某个地方使用free(x)释放了x所指向的内存空间(free操做只是释放该指针所指向的内存,并不会将指针置为NULL),可是忘记将x指针置为NULL,那么x将还会指向这块内存,假如后面经过if(x==NULL) 来进行判断,显然x不等于NULL,就可能出现非法访问x所指向的内存(可是这块内存以前已经被释放过了)的状况,显然会出现很严重的错误。code
另外须要强调的一点,有可能有多个指针指向同一块内存,因此这种时候若是释放了内存空间的话,必须保证全部指向这块内存的指针都再也不指向这块内存,能够是置为NULL,固然也可使其指向其余合法的内存地址。对象
下面以一个简单的例程4展现free以后的指针将还指向原来的内存内存
#include<stdio.h> int main(){ int *x; x = (int*)malloc(sizeof(int)); //为x动态分配一块内存,内存大小为int型大小 *x = 3; //将3存储到分配的内存中去 printf("%d\n ", x); //以整数形式输出指针,也就是对应的内存的地址 printf("%d\n", *x); //输出内存中存储的数值 free(x); if(x != NULL){ printf("%d\n", x); //将内存释放后再输出指针的值 printf("%d\n", *x); //看看释放了内存以后还能不能经过指针访问这块内存 } x=NULL; printf("%d\n", x); //将指针置为NULL以后再输出指针的值 printf("%d\n", *x); //看看将指针置为NULL以后,再用*x会有什么效果 return 0; }
使用gcc编译的时候(我暂时将源文件命名为test1.c,使用gcc test1.c -o test1来编译),会有关于不正确使用指针的警告信息,可是并非语法错误,因此仍是能够编译经过,可是这就存在潜藏的大问题了。资源
最后运行输出的结果是:
29540368
3
29540368
0
0
Segmentation fault (core dumped)
因此能够清晰的看出,经过指针释放了动态分配的内存以后,指针仍是指向原来的地址,还能够访问原来的地址(不过原来的地址中的值可能变了),而最后将指针置为NULL以后,显然指针再也不指向原来的地址,并且若是这时候再想经过指针访问对应的内存,就会报段错误。
这个程序演示了几种不规范的使用指针的方法:
- 使用free释放了内存以后没有将指针置为NULL或者将指针再次指向另外一个合法的内存而致使的再次访问到已经被释放了的内存的状况。Free以后而没有将指针置为NULL或者再次指向合法的地址,而后根据`if(pointer != NULL)`来进行判断指针是否合法实际上是没有意义的。 - 指针置为NULL以后错误的经过*运算符取指针所指向内存的数据而致使的段错误。
另外,关于这个小的测试程序,有一个关于printf使用的问题,我已经在问答网站上问了,请参见:http://segmentfault.com/q/101...
补充:使用Delphi开发语言进行开发的时候,Delphi的类、对象名也须要注意释放的问题。直接以一个例程5讲解
var objectA : ClassA; {声明一个ClassA类型的变量} begin objectA := ClassA.Create; {为objectA建立实体,要注意的是Delphi的对象的变量名其实就是一个指针} {因此这里会建立一个实体,objectA实际上是指针,会指向这个实体} {使用对象进行一些操做} objectA.Free; {最后释放变量} objectA:= nil; {别忘了将变量置为nil,由于Delphi中的类的对象名就是指针} end;
说明:Delphi的面向对象编程中,一个对象名就至关于一个指针!
屡次malloc可是没有对应的释放次
这样一个C语言的例子
#include<stdio.h> int main(){ int *x; int i; for(i=0; i<=100; i++) x = (int *)malloc(size(int)); free(x); x = NULL; return 0; }
在这个例子中,屡次用malloc分配内存,而后每次将新分配的内存的地址赋值给x,可是x只能指向一个地址,因此最后x只能指向最后一次分配的内存的地址,因此也就只能释放最后一次分配的内存,而前99次分配的内存由于丢失了地址,因此没有办法释放,只能形成内存泄漏。
相似的dephi的例子多是这样的:
var i: Integer; objectA: ClassA; begin for i:=0 to 100 do begin objectA:= ClassA.Create; end; {...} objectA.Free; objectA:= nil; end;
这个delphi的程序,每次ClassA.Create就在内存中建立一个实体,而后将objectA指向这个实体的内存,可是和上面的C程序相似,objectA只能指向一个地址,因此只能指向最后一次建立的对象实体的地址,因此前面99个建立的对象由于丢失了地址,因此没有办法释放,因此也就形成了内存泄露。
固然也有时候能够这样使用:就是在某种状况下,delphi的某个线程类是能够运行结束的(不是无限循环执行的),而且将FreeOnTerminate设为True(表示线程运行结束以后会自动释放线程的相关资源),这时候能够以这样的方式建立多个不使用指针保存其内存实体地址的线程类。可是这样的状况也实在是少,就算是能够采起这样的策略,我仍是建议选择用个指针保存其地址以保证更为稳妥。总而言之,Delphi面向对象的一个好的编程规范是:建立类的对象实体,要有一个对象名(指针)保存这个实体的地址。
动态分配的内存尚未释放就直接将指向该内存的指针置为NULL
这样的一个C语言的例子
#include<stdio.h> int main(){ int *x; x = (int *)malloc(sizeof(int)); //... x = NULL; //free(x); return 0; }
这样的程序分配了内存空间,而后x指向这块内存(注意不要这样表述:使用malloc为x指针分配了空间,这样的表达方式是错误的,而应该是malloc分配了内存空间,而后x指针指向这块内存空间)。可是却将x置为NULL,这时候原来分配的内存空间的地址就丢失了,就没有办法对其释放,因此就会致使内存泄漏。
另外关于free的参数是空指针的问题(注意也不要表达成:free释放指针,应该是:释放指针所指向的内存空间),我在问答网站上提问了,请参见:http://segmentfault.com/q/101...
相似的Delphi的程序多是这样的:
var ObjectA: ClassA; begin ObjectA:= ClassA.Create; {...} ObjectA:= nil; //ObjectA.Free; end;
你可能会说,这种错误太明显了,我怎么可能会犯呢?
可是,当一个项目特别大的时候,有上万行,甚至数十万行的代码的时候,你还能保证不由于你的不细心或者各类意料以外的偶然而不出现这种状况吗?