现在的位置: 首页 > 综合 > 正文

C/C++野指针

2017年08月04日 ⁄ 综合 ⁄ 共 3438字 ⁄ 字号 评论关闭

C/C++野指针

一、莫名的恐惧感

        对于指针确实有种莫名的恐惧感,从刚开始学习的时候就被老师灌输的概念是指针功能很强大,但是用得不够好,会有很大的副作用。什么叫用得够好?初学者谁都不会用,那且不是都不能用了?如果都这样,指针都没人用了,还要指针做什么?

        陷入了上述困局的原因在于我们的这种莫名的恐惧感,指针如此之神秘,以至于如我一样大多数的人都望而生畏,无所适从,被迫放弃。

二、野指针

       诚如当初老师的忠告一样, 指针是个很强大的工具,可是正因为它太强大,所以要操作它不是件易事。操作不当造成的野指针,甚至会引起系统死机等比较严重的后果。

        如果程序定义了一个指针,就必须要立即让它指向一个我们设定的空间或者把它设为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++代码:

  1. void function( void )  
  2. {  
  3.     char* str = new char[100];  
  4.     delete[] str;  
  5.     // Do something  
  6.     strcpy( str, "Dangerous!!" );  
  7. }         

        之所以说其危险,是因为这是一段完全合乎语法的代码,编译的时候完美得一点错误也不会有,然而当运行到strcpy一句的时候,问题就会出现,因为在这之前,str的空间已经被delete掉了,所以strcpy当然不会成功。对于这种类似的情况,在林锐博士的书中有过介绍,称其为“野指针”。

 例子2:——指针初始化引出的问题

 对指针初始化时,引出的应该注意的问题小结:
(1)先看例子:

  1. #include <iostream.h>  
  2.   
  3. void main()  
  4. {  
  5. char *p,*p1="hello first!";  
  6. while((*(p++) = *(p1++)) != 0);  
  7.   
  8. cout<<p<<endl;  

 错处:

p定义时没有初始化,p是指向不定,是一个野指针。
p++可能引用得空间为非法的。
 编译时不会出错,但运行时会造成程序崩溃。

(2)把上面的p初始化为NULL

  1. #include <iostream.h>  
  2.   
  3. void main()  
  4. {  
  5. char *p=NULL,*p1="hello first!";  
  6. while((*(p++) = *(p1++)) != 0);  
  7.   
  8. cout<<p<<endl;  
  9. }

也错:

NULL表示没有任何指向。p++没有任何意义,运行时会造成程序崩溃。这里就应该想到不能对NULL的指针进行直接操作。

(3)现在为p初始化为" ":

  1. void main()  
  2. {  
  3. char *p=" ",*p1="hello first!";  
  4.   
  5. while((*(p++) = *(p1++)) != 0);  
  6.   
  7. cout<<p<<endl;  
  8. }

还错:

p指向的是一个const常量的内容,可以通过*(p++)形式引用该值,但是不能改变它指向const的内容。

(4)

  1. #include <iostream.h>  
  2. #include <string.h>  
  3. void main()  
  4. {  
  5.  char c[]="";  
  6.  char *p=c,*p1="hello first!";  
  7.  char *p_start=p;  
  8.  while((*(p++) = *(p1++)) != 0);  
  9.    
  10.  cout<<c<<endl;  

问题:  

此时数组是一系列的变量了,也就是p一有指向,二不是指向常量的而是指向变量的。所以按理应该行的。问题出在c太小,造成了数组越界,所以错掉!把c的大小改来不比"hello first!"小就行了。

5)对于的就想到用new来初始化了:

  1. #include <iostream.h>  
  2. #include <string.h>  
  3. void main()  
  4. {  
  5.  char *p,*p1="hello first!";  
  6.  p=new char;  
  7.  char *p_start=p;  
  8.  while((*(p++) = *(p1++)) != 0);  
  9.    
  10.  cout<<p_start<<endl;  
  11. }       

        现在就可以了,哈,不过,我认为在new时最好还是把它的大小给指出来,如new char[strlen(p1)+1];如果不指定大小,我想p++会指向一些已用得地址,而这些地址又不能随便改,就会造成诸如野指针样程序崩溃了。

小结:对于前面的问题,不防这样来写:

  1. #include <iostream.h>  
  2. #include <string.h>  
  3. void main()  
  4. {  
  5.  char *p=NULL,*p1="hello first!";  
  6.  p=new char[strlen(p1)+1];  
  7.  //p=new char;            //觉得最好别这样子,new char只相当于得到一个char对  
  8.  char *p_start=p;        //象,分配一个字符的空间。  
  9.  while((*(p++) = *(p1++)) != 0);  
  10.    
  11.  cout<<p_start<<endl;  

造成野指针的原因

      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是非常危险的,如必须这样做,请写注释到程序里面,免得忘记。  

抱歉!评论已关闭.