[ZZ] 空指针问题

指针究竟指向了内存的哪个地方?
NULL在"stdio.h"中被宏定义为0(或其他什么常数〈视编译器而定〉)

指针指向你进程私有地址的0地址,,它不会被分配出去,主要的不是null指在哪,而是如果指向null,我们的代码就可以用if(ptr)来判断它是不是有效的指针,不过,如果这个指针不是指向0,也有可能不是个有效的指针,所以建议程序员在定义指针时把它初始化为0。

NULL只是一个概念,叫作值,其值本身没有任何含义,可以用0代替,也可以用1,...代替,只要这些值不会与系统实际的有效地址冲突即可。
因此,本人在此再次强调,不要自作聪明地认为NULL就是0,要判断的时候还是老老实实地与NULL做比较,别想当然地用什么!ptr之类的写法,因为在某个特定环境下,NULL可能不是0,而系统函数返回的是NULL不是0,那时,你的函数就会出现莫名其妙的错误。所以,养成良好的习惯是非常重要的。

NULL指针的值一定是0,这点可参照C语言标准。以下来自C99(WG14/N843 199:


       [#3] An integer constant expression with  the  value  0,  or
       such  an  expression  cast  to type void *, is called a null
       pointer constant.46)  If a null pointer constant is assigned
       to or compared for equality to a pointer,  the  constant  is
       converted to a pointer of that type.  Such a pointer, called
       a null pointer,  is  guaranteed  to  compare  unequal  to  a
       pointer to any object or function.
.....

       46)The macro NULL is defined in <stddef.h>; as a null pointer
          constant; see 7.17.

虚拟内存是有权限控制的。0x0 这个位置是规定了不可读写,因此试图读写的时候必然引发 Access Volitation
MEM中有个地址转换表!指针指向一个内核保留的只读地址!由MEM(内存管理)完成映射

如果仅仅声明一个指针,而没有任何赋值,那么这个指针是野指针,它会指到VM的任何位置,碰到异常操作,比如对只读区写操作,就会引起硬件中断产生core,也就是通常的段错误
良好的编程风格是将指针永远都可控,也就是这个指针的地址,程序可控,通常,对于不使用或初始的指针都将其地址置为0,这是约定俗成的,就如同,我们经常使用的进制一样,你非用一个别人都不用的进制表示数,那也随你,只是别人觉得怪而已。再比如,用free释放完指针后,相信大家都会将指针置成NULL或0,就是为了再使用这个指针时,便于判断。指针的地址为0,操作起来就非常方便,比较位操作等,都可对应到机器码,这也就体现了“高级汇编”的美誉。用NULL宏,仅仅是为了可读性,编译器会进行优化的。
对于将NULL定义成某个地址,然后进行比较,相对NULL为0地址,然后比较,性质是相同的,在执行过程中,如果重新定义的地址为可操作,可能会对程序的逻辑流程产生影响。
 
把帖子中出现的几个问题总结了一下,结合 C99 标准试图给出较为明确的答案:

1. 什么是指针常量(null pointer constant)?

[6.3.2.3-3] An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.

这里告诉我们:0、0L、'/0'、3 - 3、0 * 17 (它们都是“integer constant expression”)以及 (void*)0 等都是指针常量(注意 (char*) 0 不叫指针常量,只是一个指针值)。至于系统选取哪种形式作为指针常量使用,则是实现相关的。一般的 C 系统选择 (void*)0 或者 0 的居多(也有个别的选择 0L);至于 C++ 系统,由于存在严格的类型转化的要求,void* 不能象 C 中那样自由转换为其它指针类型,所以通常选 0 作为指针常量,而不选择 (void*)0。

2.什么是指针null pointer)?

[6.3.2.3-3] If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

因此,如果 p 是一个指针变量,则 p = 0;、p = 0L;、p = '/0';、p = 3 - 3;、p = 0 * 17; 中的任何一种赋值操作之后(对于 C 来说还可以是 p = (void*)0;), p 都成为一个指针,由系统保证指针不指向任何实际的对象或者函数。反过来说,任何对象或者函数的地址都不可能是指针

3.什么是 NULL

[6.3.2.3-Footnote] The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant

NULL 是一个标准规定的宏定义,用来表示指针常量。因此,除了上面的各种赋值方式之外,还可以用 p = NULL; 来使 p 成为一个指针

 

4.指针null pointer)指向了内存的什么地方(指针的内部实现)?

标准并没有对指针指向内存中的什么地方这一个问题作出规定,也就是说用哪个具体的地址值(0x0 地址还是某一特定地址)表示指针取决于系统的实现。我们常见的指针一般指向 0 地址,即指针的内部用全 0 来表示(zero null pointer,零指针);也有一些系统用一些特殊的地址值或者特殊的方式表示指针(nonzero null pointer,非零指针),具体请参见 C FAQ。

幸运的是,在实际编程中不需要了解在我们的系统上空指针到底是一个 zero null pointer 还是 nonzero null pointer,我们只需要了解一个指针是否是指针就可以了——编译器会自动实现其中的转换,为我们屏蔽其中的实现细节。注意:不要把指针的内部表示等同于整数 0 的对象表示——如上所述,有时它们是不同的。

 

5.如何判断一个指针是否是一个指针

这可以通过与指针常量或者其它的指针的比较来实现(注意与指针的内部表示无关)。例如,假设 p 是一个指针变量,q 是一个同类型的指针,要检查 p 是否是一个指针,可以采用下列任意形式之一——它们在实现的功能上都是等价的,所不同的只是风格的差别。

指针变量 p 是指针的判断:
if ( p == 0 )
if ( p == '/0' )
if ( p == 3 - 3 )
if ( p == NULL ) /* 使用 NULL 必须包含相应的标准库的头文件 */
if ( NULL == p )
if ( !p )
if ( p == q )
...

指针变量 p 不是指针的判断:
if ( p != 0 )
if ( p != '/0' )
if ( p != 3 - 3 )
if ( p != NULL ) /* 使用 NULL 必须包含相应的标准库的头文件 */
if ( NULL != p )
if ( p )
if ( p != q )
...

 

6.可以用 memset 函数来得到一个指针吗?

这个问题等同于:如果 p 是一个指针变量,那么

memset( &p, 0, sizeof(p) ); 和 p = 0;

是等价的吗?

答案是否定的,虽然在大多数系统上是等价的,但是因为有的系统存在着“非零指针” (nonzero null pointer),所以这时两者不等价。由于这个原因,要注意当想将指针设置为指针的时候不应该使用 memset,而应该用指针常量或指针对指针变量赋值或者初始化的方法。

 

7.可以定义自己的 NULL 的实现吗?兼答"NULL 的值可以是 1、2、3 等值吗?"类似问题

[7.1.3-2] If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined.

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

 

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

malloc 函数是标准 C 规定的库函数。在标准中明确规定了在其内存分配失败时返回的是一个 “null pointer”(指针):

[7.20.3-1] If the space cannot be allocated, a null pointer is returned.

对于指针值,一般的文档(比如 man)中倾向于用 NULL 表示,而没有直接说成 0。但是我们应该清楚:对于指针类型来说,返回 NULL 和 返回 0 是完全等价的,因为 NULL 和 0 都表示 “null pointer”(指针)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值