一、 莫名的恐惧感
对于指针确实有种莫名的恐惧感,从刚开始学习的时候就被老师灌输的概念是指针功能很强大,但是用得不够好,会有很大的副作用。什么叫用得够好?初学者谁都不会用,那且不是都不能用了?如果都这样,指针都没人用了,还要指针做什么?
陷入了上述困局的原因在于我们的这种莫名的恐惧感,指针如此之神秘,以至于如我一样大多数的人都望而生畏,无所适从,被迫放弃。
二、野指针
诚如当初老师的忠告一样, 指针是个很强大的工具,可是正因为它太强大,所以要操作它不是件易事。操作不当造成的野指针,甚至会引起系统死机等比较严重的后果。
如果程序定义了一个指针,就必须要立即让它指向一个我们设定的空间或者把它设为NULL,如果没有这么做,那么这个指针里的内容是不可预知的,即不知道它 指向内存中的哪个空间(即野指针),它有可能指向的是一个空白的内存区域,可能指向的是已经受保护的区域,甚至可能指向系统的关键内存,如果是那样就糟 了,也许我们后面不小心对指针进行操作就有可能让系统出现紊乱,死机了。所以我们必须设定一个空间让指针指向它,或者把指针设为NULL,这是怎么样的一 个原理呢,如果是建立一个与指针相同类型的空间,实际上是在内存中的空白区域中开辟了这么一个受保护的内存空间,然后用指针来指向它,那么指针里的地址就 是这个受保护空间的地址了,而不是不可预知的啦,然后我们就可以通过指针对这个空间进行相应的操作了;如果我们把指针设为NULL,我们在头文件定义中的 #define NULL 0 可以知道,其实NULL就是表示0,那么我们让指针=NULL,实际上就是让指针=0,如此,指针里的地址(机器数)就被初始化为0了,而内存中地址为0 的内存空间……不用多说也能想象吧,这个地址是特定的,那么也就不是不可预知的在内存中乱指一气的野指针了。
还应该注意的是,free和delete只是把指针所指的 内存给释放掉,但并没有把指针本身干掉。指针p被free以后其地址仍然不变(非NULL),只是该地址对应的内存是垃圾,p成了“野指针”。如果此时不 把p设置为NULL,会让人误以为p是个合法的指针。用free或delete释放了内存之后,就应立即将指针设置为NULL,防止产生“野指针”。内存 被释放了,并不表示指针会消亡或者成了NULL指针。(而且,指针消亡了,也并不表示它所指的内存会被自动释放。)
三 例说野指针
例子1:
首先请诸位看以下一段“危险”的C++代码:
void function( void )
{
char* str = new char[100];
delete[] str;
// Do something
strcpy( str, "Dangerous!!" );
}
之所以说其危险,是因为这是一段完全合乎语法的代码,编译的时候完美得一点错误也不会有,然而当运行到strcpy一句的时候,问题就会出现,因为在这之 前,str的空间已经被delete掉了,所以strcpy当然不会成功。对于这种类似的情况,在林锐博士的书中有过介绍,称其为“野指针”。
例子2:——指针初始化引出的问题
对指针初始化时,引出的应该注意的问题小结:
(1)先看例子:
include
void main()
{
char p,p1="hello first!";
while((*(p++) = *(p1++)) != 0);
cout<
}
错处:
p定义时没有初始化,p是指向不定,是一个野指针。
p++可能引用得空间为非法的。
编译时不会出错,但运行时会造成程序崩溃。
(2)把上面的p初始化为NULL
include
void main()
{
char p=NULL,p1="hello first!";
while((*(p++) = *(p1++)) != 0);
cout<
}
也错:
NULL表示没有任何指向。p++没有任何意义,运行时会造成程序崩溃。这里就应该想到不能对NULL的指针进行直接操作。
(3)现在为p初始化为" ":
void main()
{
char p=" ",p1="hello first!";
while((*(p++) = *(p1++)) != 0);
cout<
}
还错:
p指向的是一个const常量的内容,可以通过*(p++)形式引用该值,但是不能改变它指向const的内容。
(4)
include
include
void main()
{
char c[]="";
char p=c,p1="hello first!";
char p_start=p;
while(((p++) = *(p1++)) != 0);
cout<
}
问题:
此时数组是一系列的变量了,也就是p一有指向,二不是指向常量的而是指向变量的。所以按理应该行的。问题出在c太小,造成了数组越界,所以错掉!把c的大小改来不比"hello first!"小就行了。
5)对于的就想到用new来初始化了:
include
include
void main()
{
char p,p1="hello first!";
p=new char;
char p_start=p;
while(((p++) = *(p1++)) != 0);
cout<
}
现在就可以了,哈,不过,我认为在new时最好还是把它的大小给指出来,如new char[strlen(p1)+1];如果不指定大小,我想p++会指向一些已用得地址,而这些地址又不能随便改,就会造成诸如野指针样程序崩溃了。
小结:对于前面的问题,不防这样来写:
include
include
void main()
{
char p=NULL,p1="hello first!";
p=new char[strlen(p1)+1];
//p=new char; //觉得最好别这样子,new char只相当于得到一个char对
char p_start=p; //象,分配一个字符的空间。
while(((p++) = *(p1++)) != 0);
cout<
}
四 造成野指针的原因
1、指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的默认值是随机的,它会乱指一气。
2、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
3、指针操作超越了变量的作用范围。这种情况让人防不胜防。
五 正确使用指针
通常避免野指针的办法是正确的使用指针
1.声明一个pointer的时候注意初始化为null :
int* pInt = NULL;
2.分配完内存以后注意ASSERT:
pInt = new int[num];
ASSERT(pInt != NULL);
3.删除时候注意用对操作符:
对于new int类型的,用delete
对于new int[]类型的,用delete []
4.删除完毕以后记得给他null地址:
delete [] pInt;
pInt = NULL;
5.记住,谁分配的谁回收,不要再一个函数里面分配local pointer,送到另外一个函数去delete。
6.返回local address是非常危险的,如必须这样做,请写注释到程序里面,免得忘记。