C++的空指针、野指针和指针赋值NULL.md
1.空指针和野指针
int *ip = 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
在现在的C/C++中定义的NULL即为0,⽽C++中的true为≠0,所以此时的if(ip)和if(ip != NULL)是等效的。null官方更新地址
NULL指针
NULL是⼀个标准规定的宏定义,⽤来表⽰空指针常量。在C++⾥⾯被直接定义成了整数⽴即数的0,⽽在没有__cplusplus定义的前提下,就被定义成⼀个值是0的 void* 类型的指针常量
零指针
零值指针,是值为0的指针,可以是任何⼀种类型的指针,可以是通⽤变体类型 void,也可以是 char, int* 等等。
在C++⾥⾯,任何⼀个概念都以⼀种语⾔内存公认的形式表现出来,例如std::vector会提供⼀个empty()⼦函数来返回容器是否为空,然⽽对于⼀个基本数值类型(或者说只是⼀个类似整数类型的类型)我们不可能将其抽象成⼀个类(当然除了auto_ptr等智能指针)来提供其详细的状态说明,所以我们需要⼀个特殊值来做为这种状态的表现。
C++标准规定,当⼀个指针类型的数值是0时,认为这个指针是空的。(我们在其它的标准下或许可以使⽤其它的特殊值来定义我们需要的NULL实现,可以是1,可以是2,是随实现要求⽽定的,但是在标准C++下⾯我们⽤0来实现NULL指针)
空指针指向内存的什么地⽅
标准并没有对空指针指向内存中的什么地⽅这⼀问题作出规定,也就是说⽤哪个具体地址值表⽰空指针取决于系统实现。我们常见的空指针⼀般指向0地址,即空指针的内部⽤全0来表⽰(zero null pointer,零空指针);也有⼀些系统⽤⼀些特殊的地址值或者特殊的⽅式表⽰空指针(nonzero null pointer,⾮零空指针),具体参见 C FAQ。
在实现编程中不需要了解在我们的系统上空指针到底是⼀个zero null pointer还是 nonzero null pointer,我们只需要了解⼀个指针是否是空指针就可以了——编译器会⾃动实现其中的转换,为我们屏蔽其中的实现细节。注意:不要把空指针的内部实现表⽰等同于整数0的对象表⽰——如上所述,有时它们是不同的。
对空指针实现的保护政策
逻辑地址和物理地址
既然我们选择了0作为空的概念。在⾮法访问空的时候我们需要保护以及报错。因此,编译器和系统提供了很好的政策。
我们程序中的指针其实是windows内存段偏移后的地址,⽽不是实际的物理地址,所以不同的地址中的零值指针指向的同⼀个0地址,其实在内存中都不是物理内存的开端的0,⽽是分段内存的开端,这⾥我们需要简单介绍⼀下windows下的内存分配和管理制度:
windows下,执⾏⽂件(PE⽂件)在被调⽤后,系统会分配给它⼀个额定⼤⼩的内存段⽤于映射这个程序的所有内容(就是磁盘上的内容)并且为这个段进⾏新的偏移计算,也就是说我们的程序中访问的所有near指针都是在我们“⾃家”的段⾥⾯的,当我们需要访问far指针的时候,我们其实是跳出了“⾃家的院
⼦”到了他⼈的地⽅,我们需要⼀个段偏移资质来完成新的偏移(⼈家家⾥的偏移)所以我们的指针可能是OE02:0045就是告诉我们要访问0E02个内存段的0045号偏移,然后windows会⾃动给我们到0E02段的开始偏移,然后为我们计算真实的物理地址。
所以程序A中的零值指针和程序B中的零值指针指向的地⽅可能是完全不同的。
空指针赋值分区
这⼀分区是进程的地址空间中从0x00000000 到 0x0000FFFF 的闭区间(64K 的内存⼤⼩),这 64K 的内存是⼀块保留内存,不能被程序动态内存分配器分配,不能访问,也不能使⽤,保留该分区的⽬的是为了帮助程序员捕获对空指针的赋值。如果进程中的线程试图读取或者写⼊位于这⼀分区内的内存地址,就会引发访问违规。
为什么空指针访问会出现异常
归根结底,程序中所使⽤的数据都需要从物理设备上获取,即程序中的数据需要从⼀个真实的物理地址中读取或者写⼊。所以当⼀个指针的逻辑地址可以通过计算能够准确⽆误的映射到⼀个正确的物理地址上时,这时候数据的访问就是正确的,程序的执⾏也没有任何问题。如果⼀个指针为空指针,那么该指针所指向的逻辑地址空间位于空指针赋值分区的区间上。空指针赋值分区上的逻辑地址没有物理存储器与之对应,因⽽访问时就会产⽣违规访问的异常。
野指针
野指针不是空指针,是⼀个指向垃圾内存的指针。
形成原因
1.指针变量没有被初始化。
任何指针变量被刚创建时不会被⾃动初始化为NULL指针,它的缺省值是随机的。所以,指针变量在创建的同时应当被初始化,要么将指针
设置为NULL,要么让它指向合法的内存。例如:
char* p = NULL;
char* str = (char*)malloc(1024);
2.指针被free或者delete之后,没有设置为NULL,让⼈误以为这是⼀个合法指针。
free和delete只是把指针所指向的内存给释放掉,但并没有把指针本⾝给清理掉。这时候的指针依然指向原来的位置,只不过这个位置的内存数据已经被毁⼫灭迹,此时的这个指针指向的内存就是⼀个垃圾内
存。但是此时的指针由于并不是⼀个NULL指针(在没有置为NULL的前提下),在做如下指针校验的时候
if(p != NULL)
会逃过校验,此时的p不是⼀个NULL指针,也不指向⼀个合法的内存块,造成会⾯程序中指针访问的失败。
3.指针操作超越了变量的作⽤范围。
由于C/C++中指针有++操作,因⽽在执⾏该操作的时候,稍有不慎,就容易指针访问越界,访问了⼀个不该访问的内存,结果程序崩溃
另⼀种情况是指针指向⼀个临时变量的引⽤,当该变量被释放时,此时的指针就变成了⼀个野指针,如下
A *p; // A为⼀个⾃定义对象
{
A a;
p = &a; // 注意 a 的⽣命期,只在这个程序块中(花括号⾥⾯的两⾏),⽽不是整个test函数
}
p->Func();  // p是“野指针”
2.指针赋值NULL
C++标准规定delete空指针是合法的
将指针delete后赋值NULL是防⽌野指针的出现,因为delete是向系统申明这段内存不再使⽤,但是其中的内容并没有情况。这个时候if (NULL != p)
是失效的,所以要赋值NULL。
对于局部的临时变量delete⾜矣,不需要再赋值NULL

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。