1.空指针和野指针

http://blog.csdn.net/fu_zk/article/details/21030607

空指针常量

一个表示0值的整数常量,叫做空指针常量。例如:0、0L、1-1(它们都是值为0的整数常量表达式)以及(void)0、void NULL 都是空指针常量,空指针常量可以赋值给任何指针类型,因为它是变体类型(void*)。但是我们更倾向于使用NULL表示这个空指针常量。对于其它方式(比如0)来表示空指针常量虽然不会产生任何问题,但是在根本意义上并不符合空指针常量的定义。因为空指针常量的存在意义还在强调它并不指向任何对象(后面会讲细节)。

空指针

空指针不指向任何实际的对象或者函数。反过来说,任何对象或者函数的地址都不可能是空指针。

空指针是一个特殊的指针,因为这个指针不指向任何地方。这意味任何一个有效的指针如果和空指针进行相等的比较运算时,结果都是false。

在程序中,得到一个空指针最直接的方法就是运用预定义的NULL,这个值在多个头文件中都有定义。

如果要初始化一个空指针,我们可以这样,

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是一个标准规定的宏定义,用来表示空指针常量。在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

C++的空指针、野指针和指针赋值NULL.md的更多相关文章

  1. [转载]delete指针之后应该赋值NULL

    首先,C++标准规定:delete空指针是合法的,没有副作用.但是,delete p后,只是释放了指针指向的内存空间.p并不会自动被置为NULL,而且指针还在,同时还指向了之前的地址. 问题来了,对一 ...

  2. Rust语言——无虚拟机、无垃圾收集器、无运行时、无空指针/野指针/内存越界/缓冲区溢出/段错误、无数据竞争

    2006年,编程语言工程师Graydon Hoare利用业余时间启动了Rust语言项目.该项目充分借鉴了C/C++/Java/Python等语言的经验,试图在保持良好性能的同时,克服以往编程语言所存在 ...

  3. delete指针以后应赋值为NULL——QT deletelater指针以后也同样要马上赋值为NULL

    delete p后,只是释放了指针指向的内存空间.p并不会自动被置为NULL,而且指针还在,同时还指向了之前的地址 delete NULL编译器不会报错(因为delete空指针是合法的) 例: 对一个 ...

  4. C语言_指针变量的赋值与运算,很详细

    指针变量的赋值 指针变量同普通变量一样,使用之前不仅要定义说明, 而且必须赋予具体的值.未经赋值的指针变量不能使用, 否则将造成系统混乱,甚至死机.指针变量的赋值只能赋予地址, 决不能赋予任何其它数据 ...

  5. C++ 指针悬挂和赋值操作符的重载,拷贝构造函数实现

    指针悬挂: 问题:使用new申请的内存内存空间无法访问,也无法释放. 原因:直接对指向new申请的存储空间的指针变量进行赋值修改 后果:失去了原来的地址,原来的空间无法访问也无法释放,造成内存泄漏 还 ...

  6. [C]关于函数指针参数的赋值

    问题 在有一次尝试用stat()函数获取文件属性的时候,发现如果直接声明一个指针,然后把这个指针作为参数传给函数,会导致函数执行失败,原代码: #include <sys/stat.h> ...

  7. [错误记录_C] 还未给指针变量正确赋值的情况下,就使用它的值

    错误的代码: 错误的结果:  错误原因分析: 在使用(1) 将pB,pC的值赋给pA的lchild和rchild时: 还未给指针变量pB和pC赋值,现在pB和pC中存的是个垃圾值 Note: (2)- ...

  8. C++指针之间的赋值与转换规则总结

    C++指针之间的赋值与转换规则总结 Note:以下结论不适用于类的成员函数指针,关于类的成员函数指针会单独讨论. 一.任何类型的指针变量均可直接赋值给const void * 任何类型的非const指 ...

  9. c/c++ 复习基础要点01-const指针、指针函数 函数指针、new/delete与malloc/free区别与联系

    1.      引用本身是有指针实现的:引用为只读指针 例子: int d=123; int& e=d;    //引用 int * const e=d; //只读指针,e指向d,不可修改e指 ...

随机推荐

  1. MySQL性能指标及计算方法(go)

    绝大多数MySQL性能指标可以通过以下两种方式获取: (1)mysqladmin 使用mysqladmin extended-status命令获得的MySQL的性能指标,默认为累计值.如果想了解当前状 ...

  2. BEGIN-4_蓝桥杯_Fibonacci数列

    问题描述 Fibonacci数列的递推公式为:Fn=Fn-+Fn-,其中F1=F2=. 当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. 输入格式 输入包含一个整数n. 输 ...

  3. php安装ZendGuardLoader扩展问题

     1,安装ZendGuardLoader:wget http://downloads.zend.com/guard/6.0.0/ZendGuardLoader-70429-PHP-5.4-linux- ...

  4. bzoj 4842: [Neerc2016]Delight for a Cat

    Description ls是一个特别堕落的小朋友,对于n个连续的小时,他将要么睡觉要么打隔膜,一个小时内他不能既睡觉也打隔膜 ,因此一个小时内他只能选择睡觉或者打隔膜,当然他也必须选择睡觉或打隔膜, ...

  5. bzoj1085 骑士精神

    Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2, ...

  6. ASP.NET Web Pages:目录

    ylbtech-.Net-ASP.NET Web Pages:目录 1. 官网返回顶部 1. https://www.asp.net/web-pages 2. https://msdn.microso ...

  7. JQUERY dialog的用法详细解析

    本篇文章主要是对JQUERY中dialog的用法进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助 今天用到了客户端的对话框,把 jQuery UI 中的对话框学习了一下. 准备 jQ ...

  8. pandas的to_csv函数

    分隔符 sep : Field delimiter for the output file (default ”,”) dt.to_csv('C:/Users/think/Desktop/Result ...

  9. guess_age

    age_shanshan = 18count = 3num = 0while num < count: age = int(input("age:")) if age == ...

  10. sql server不要插入大数据,开销太大

    sql server或者说关系型数据库中不要做一个字段存储大数据量的设计,比如要插入3000w条数据,然后每条数据中有一个文章字段,这个字段每条大概都需要存储几m的数据,那么算下来这个表就得有几百个G ...