【C++】NULL指针、零指针、空指针、野指针

参考博客1点击原文链接
参考博客2点击原文链接

1.NULL指针、零指针、空指针

1.1空指针常量

一个表示0值的空指针常量,叫做空指针常量。例如0、0L、’\0’,3-3、0*17、(void*)0,都是空指针常量((char*)0不叫空指针常量、只是一个空指针值)。系统选择哪种形式作为空指针常量使用、则是实现相关的。一般的C系统选择(void*)0或者0的居多;至于C++系统,由于存在严格的类型转换的要求,void*不能向C那样自由转换为其它指针类型,所以通常选0作为空指针常量(C++标准推荐),而不选择 (void*)0

1.2空指针

空指针不指向任何实际的对象或者函数。反过来说,任何对象或者函数的地址都不可能是空指针。
空指针是一个特殊的指针,因为这个指针不指向任何地方。这意味任何一个有效的指针如果和空指针进行相等的比较运算时,结果都是false
空指针只是一种编程概念,就如一个容器可能有空和非空两种基本状态。

1.3NULL指针

NULL是一个标准规定的宏定义,用来表示空指针常量。在C++里面被直接定义成了整数立即数的0,而在没有__cplusplus定义的前提下,就被定义成一个值是0的 void* 类型的指针常量。
如果要初始化一个空指针,我们可以这样,

int *p = NULL;

校验一个指针是否为一个有效指针时,我们更倾向于使用这种方式,

if(ip != NULL)  

而不是

if(ip)

为什么有人会用if(ip)这种方式校验一个指针非空,而且在C++中不会出现错误呢?而且现在很多人都会这样写。
原因是这样的,

/* Define   NULL   pointer   value   */
#ifndef   NULL   
#   ifdef   __cplusplus   
#     define   NULL      0   
#   else   
#     define   NULL      ((void   *)0)   
#   endif   
#endif //   NULL   

1.4零指针

零值指针,是值为0的指针,可以是任何一种类型的指针,可以是通用变体类型 void*,也可以是 char*, int* 等等。
C++里面,任何一个概念都以一种语言内存公认的形式表现出来,例如std::vector会提供一个empty()子函数来返回容器是否为空,然而对于一个基本数值类型(或者说只是一个类似整数类型的类型)我们不可能将其抽象成一个类(当然除了auto_ptr等智能指针)来提供其详细的状态说明,所以我们需要一个特殊值来做为这种状态的表现。
C++标准规定,当一个指针类型的数值是0时,认为这个指针是空的。(我们在其它的标准下或许可以使用其它的特殊值来定义我们需要的NULL实现,可以是1,可以是2,是随实现要求而定的,但是在标准C++下面我们用0来实现NULL指针)

1.5空指针指向内存的位置

标准并没有对空指针指向内存中的什么地方这一问题作出规定,也就是说用哪个具体地址值表示空指针取决于系统实现。我们常见的空指针一般指向0地址,即空指针的内部用全0来表示(zero null pointer,零空指针);也有一些系统用一些特殊的地址值或者特殊的方式表示空指针(nonzero null pointer,非零空指针),具体参见 C FAQ
在实现编程中不需要了解在我们的系统上空指针到底是一个zero null pointer还是 nonzero null pointer,我们只需要了解一个指针是否是空指针就可以了——编译器会自动实现其中的转换,为我们屏蔽其中的实现细节。注意:不要把空指针的内部实现表示等同于整数0的对象表示——如上所述,有时它们是不同的。

1.6对空指针实现的保护政策

既然我们选择了0作为空的概念。在非法访问空的时候我们需要保护以及报错。因此,编译器和系统提供了很好的政策。
我们程序中的指针其实是windows内存段偏移后的地址,而不是实际的物理地址,所以不同的地址中的零值指针指向的同一个0地址,其实在内存中都不是物理内存的开端的0,而是分段内存的开端,这里我们需要简单介绍一下windows下的内存分配和管理制度:
windows下,执行文件(PE文件)在被调用后,系统会分配给它一个额定大小的内存段用于映射这个程序的所有内容(就是磁盘上的内容)并且为这个段进行新的偏移计算,也就是说我们的程序中访问的所有near指针都是在我们“自家”的段里面的,当我们需要访问far指针的时候,我们其实是跳出了“自家的院子”到了他人的地方,我们需要一个段偏移资质来完成新的偏移(人家家里的偏所E以我们的指针可0E02:0045就是告诉我们要访问0E02个内存段的0045号偏移,然后windows会自动给我们找到0E02段的开始偏移,然后为我们计算真实的物理地址。
所以程序A中的零值指针和程序B中的零值指针指向的地方可能是完全不同的。

1.7为什么通过空指针读写的时候就会出现异常

NULL指针分配的分区:其范围是从0x00000000到0**x0000FFFF**。这段空间是空闲的,对于空闲的空间而言,没有相应的物理存储器与之相对应,所以对这段空间来说,任何读写操作都是会引起异常的。空指针是程序无论在何时都没有物理存储器与之对应的地址。为了保障“无论何时”这个条件,需要人为划分一个空指针的区域,固有上面NULL指针分区。

1.8是否可以定义自己的 NULL 的实现

NULL 是标准库中的一个reserved identifier(保留标识符)。所以,如果包含了相应的标准头文件而引入了 NULL 的话,则再在程序中重新定义NULL 为不同的内容是非法的,其行为是未定义的。也就是说,如果是符合标准的程序,其 NULL 的值只能是0,不可能是除 0之外的其它值,比如 1、2、3 等。

1.9malloc 函数在分配内存失败时返回 0 还是 NULL

malloc 函数是标准 C 规定的库函数。在标准中明确规定了在其内存分配失败时返回的是一个 “null pointer”(空指针)。对于空指针值,一般的文档(比如 man)中倾向于用 NULL 表示,而没有直接说成 0。但是我们应该清楚:对于指针类型来说,返回 NULL 和 返回 0 是完全等价的,因为 NULL0 都表示 “null pointer”(空指针)。(一般系统中手册中都返回NULL
C++里面的NEW再内存失败是会抛出一个BAD_ALLOC异常。

2.野指针

野指针不是空指针,是一个指向垃圾内存的指针。

形成原因

1.指针变量没有被初始化。
任何指针变量被刚创建时不会被自动初始化为NULL指针,它的缺省值是随机的。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如:

char* p = NULL;  
char* str = (char*)malloc(1024);    

2.指针被free或者delete之后,没有设置为NULL,让人误以为这是一个合法指针。
freedelete只是把指针所指向的内存给释放掉,但并没有把指针本身给清理掉。这时候的指针依然指向原来的位置,只不过这个位置的内存数据已经被毁尸灭迹,此时的这个指针指向的内存就是一个垃圾内存。但是此时的指针由于并不是一个NULL指针(在没有置为NULL的前提下),在做如下指针校验的时候:

if(p != NULL)  

会逃过校验,此时的p不是一个NULL指针,也不指向一个合法的内存块,造成会面程序中指针访问的失败。

3.指针操作超越了变量的作用范围。
由于C/C++中指针有++操作,因而在执行该操作的时候,稍有不慎,就容易指针访问越界,访问了一个不该访问的内存,结果程序崩溃
另一种情况是指针指向一个临时变量的引用,当该变量被释放时,此时的指针就变成了一个野指针,如下

A *p; // A为一个自定义对象  
{  
    A a;  
    p = &a; // 注意 a 的生命期 ,只在这个程序块中(花括号里面的两行),而不是整个test函数  
 }  
 p->Func();  // p是“野指针”  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值