精华内容
参与话题
问答
  • 什么是空指针

    千次阅读 2014-12-08 21:08:06
    C++语言定义中说,每一种指针类型都有一个特殊值----"空指针"。 空指针在概念上不同于未初始化的指针。空指针可以确保不指向任何对象或函数;而未初始化的指针则可能指向任何地方。 空指针不是野指针。每种指针...
           C++语言定义中说,每一种指针类型都有一个特殊值----"空指针"。
           空指针在概念上不同于未初始化的指针。空指针可以确保不指向任何对象或函数;而未初始化的指针则可能指向任何地方。
           空指针不是野指针。每种指针类型都有一个空指针,而不同类型的空指针内部表示可能不尽相同。尽管程序员不必知道内部值,但编译器必须时刻明确需要哪种类型的空指针,以便在需要时加以区分。
    1)怎样在程序里获得一个空指针。

        根据语言定义,在指针上下文中的常数0,会在编译时转换为空指针。也就是说,在初始化、赋值或者比较的时候,如果一边是指针类型的值或表达式,编译器可以确定另一边的常数0为空指针并生成正确的空指针值。如下所示:

    char *p=0;
    if(p!=0)

          然而,传入函数的参数不一定当做指针环境,因为编译器可能不能识别未加修饰的0表示“指针”。

          在函数调用的上下文中,生成空指针需要明确的类型转换,强制把0看成指针。例如,UNIX系统可以调用execl接受变长的以空指针结束的字符指针参数,它应该采取如下的正确调用:

      

    execl("/bin/sh","sh","-c","date",(char* )0);
       

        如果省略最后一个参数的(char* )转换,则编译器无法知道这是一个空指针,而把它当做0。如果在作用域内有函数原型,则参数传递变为“赋值上下文”,从而可以省略类型转换;这是因为,函数原型会告知编译器这里需要指针,使之把未加修饰的0正确转换为适当的指针

         最好是在函数调用时,对所有的空指针进行类型转换。

    2)使用“if(p)” 检查空指针是否可靠

         如果空指针的内部表达不是0将会怎样?当C语言在表达式中要求布尔值时,如果表达式等于0则认为该值为假,否则为真。换言之,只要写出

    if(expr)


         无论expr是任何表达式,编译器本质上都会把它作如下处理:

    if(expr != 0)

        如果用指针p 代替expr,则if(p) 等价于 if(p != 0)。
        在此比较上下文中,编译器可以看出 0实际上是一个空指针常数,并使用正确的空指针值。这里没有任何欺骗,编译器就是这样工作的,并为二者生成完全一样的代码。空指针的内部表达无关紧要。

        所以,类似于if(p) 这样的“缩写”尽管完全合法,但被一些人认为是不好的风格。

    3)NULL是什么,它是怎样定义的

          作为一种风格,很多人不愿意在程序里看到到处出现未加修饰的 0,因此编译器在stdio.h 头文件中定义了预处理宏 NULL为空指针常数。

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

          通过定义可以看出,NULL和0 其实没有太大的差别。编译时预处理器会把所有的NULL 都还原为0,而编译还是按照上下文的描述来处理指针上下文的 0。特别是在函数调用的参数里,NULL之前的类型转换还是需要的。

    【延伸阅读】

           在使用非全零为空指针内部表达的机器上,NULL是如何定义的?

        (3.1)跟其他机器一样:定义为0 (或某种形式的 0)。

        (3.2)当程序员请求一个空指针时,无论写"0"还是“NULL”,都由编译器生成适合机器的二进制表达形式。因此,在空指针的内部表达不为 0的机器上定义NULL 为 0 跟其他机器一样合法:编译器在指针上下文看到的未加修饰的 0 都会生成正确的空指针。

    4)如果 NULL和 0作为空指针常数是等价的,到底该用哪一个?

          许多程序员认为指针上下文都应该使用NULL,以表明该值应该被看做指针。另一些人认为用一个宏定义 0,只会把事情搞的更复杂,他们倾向于使用未加修饰的 0 。

          C程序员应该明白,在指针上下文中 NULL 和 0完全等价,而未加修饰的 0也可以完全接受。任何使用 NULL的地方都应该看作一种温和的指针提示,然而程序员并不能依靠它来区分指针0 和 整数0。

    【最佳实践】虽然 NULL和 0具有相同的功能,但建议使用NULL 替代 0。这种实践有两个好处:

       (4.1)你可以认为 NULL的值改变了,比如在使用非零内部空指针的机器上,用NULL会比 0 有更好的兼容性,但事实并非如此。

       (4.2)尽管符号常量经常代替数字,以备数字的变化,但这不是NULL 替代 0 的原因。语言本身确保了源码中的 0(用于指针上下文)会生成空指针。NULL只是用做一种格式习惯。

    5)NULL可以确保是零,但空指针却不一定零

         空指针的内部(或者运行前)表达式可能不完全是零,而且对不同的指针类型可能不一样。真正的值只有编译器的开发者才关心。C++程序的作者永远看不到它们,这一点不用担心,明白就好。


    【注意】

         (5.1)空指针不一定是0,而NULL肯定是0.

          (5.2)赋值为空指针的变量,可确保变量不指向任何对象或函数。合理地使用空指针可以有效地避免内存泄露,提高程序的执行效率。

    展开全文
  • java异常处理之空指针异常

    万次阅读 2012-12-01 15:06:01
    听老师说,在以后的学习中大部分的异常都是空指针异常。所以抽点打游戏的时间来查询一下什么是空指针异常   一:空指针异常产生的主要原因如下: (1)当一个对象不存在时又调用其方法会产生异常obj.method() // ...

    听老师说,在以后的学习中大部分的异常都是空指针异常。所以抽点打游戏的时间来查询一下什么是空指针异常

     

    一:空指针异常产生的主要原因如下:
    (1)当一个对象不存在时又调用其方法会产生异常obj.method() // obj对象不存在
    (2)当访问或修改一个对象不存在的字段时会产生异常obj.method() // method方法不存在

    (3)字符串变量未初始化;
    (4)接口类型的对象没有用具体的类初始化,比如:
       List lt;会报错
       List lt = new ArrayList();则不会报错了
     当一个对象的值为空时,你没有判断为空的情况。你可以试着把下面的代码前加一行代码:
    if(rb!=null && rb!="")
    改成:
    if(rb==null);
    if(rb!==null&&rb!="") 或者if((“”).equals(rb))


    空指针的解决办法:
    重点关注报错发生的所在行,通过空指针异常产生的两条主要原因诊断具体的错误。同时为了避免空指针的发生,最好在做判断处理时将“null”或者空值放于设定的值之前。

     


    常见空指针异常的简要分析:
    (1)空指针错误 Java.lang.NullPointerException
    Java中的8种基本数据类型,变量的值可以有其默认值,加入没有对其正常赋值,java虚拟机是不能
    正确编译通过的,因此使用基本的Java数据类型一般不会是不会引起空指针异常的。实际开发中,大多数
    的空指针异常主要与对象的操作相关。

    二、Java 异常处理机制
    对于可能出现异常的代码,有两种处理办法:
    第一、在方法中用try...catch 语句捕获并处理异常,catach 语句可以有
    多个,用来匹配多个异常。例如:
    public void p(int x){
    try{
    ...
    }catch(Exception e){
    ...
    }finally{
    ...
    }}
    第二、对于处理不了的异常或者要转型的异常,在方法的声明处通过
    throws 语句抛出异常。例如:
    public void test1() throws MyException{
    ...
    if(....){
    throw new MyException();
    }}
    如果每个方法都是简单的抛出异常,那么在方法调用方法的多层嵌套调
    用中,Java 虚拟机会从出现异常的方法代码块中往回找,直到找到处理该异
    常的代码块为止。然后将异常交给相应的catch 语句处理。如果Java 虚拟机
    追溯到方法调用栈最底部main()方法时,如果仍然没有找到处理异常的代码
    块,将按照下面的步骤处理:
    第一、调用异常的对象的printStackTrace()方法,打印方法调用栈的异
    常信息。
    第二、如果出现异常的线程为主线程,则整个程序运行终止;如果非主线
    程,则终止该线程,其他线程继续运行。
    通过分析思考可以看出,越早处理异常消耗的资源和时间越小,产生影
    响的范围也越小。因此,不要把自己能处理的异常也抛给调用者。
    还有一点,不可忽视:finally 语句在任何情况下都必须执行的代码,这
    样可以保证一些在任何情况下都必须执行代码的可靠性。比如,在数据库查
    询异常的时候,应该释放JDBC 连接等等。finally 语句先于return 语句执行,
    而不论其先后位置,也不管是否try 块出现异常。finally 语句唯一不被执行的
    情况是方法执行了System.exit()方法。System.exit()的作用是终止当前正在
    运行的Java 虚拟机。finally 语句块中不能通过给变量赋新值来改变return
    的返回值,也建议不要在finally 块中使用return 语句,没有意义还容易导致
    错误。
    最后还应该注意一下异常处理的语法规则:
    第一、try 语句不能单独存在,可以和catch、finally 组成
    try...catch...finally、try...catch、try...finally 三种结构,catch 语句可以有一个
    或多个,finally 语句最多一个,try、catch、finally 这三个关键字均不能单独
    使用。
    第二、try、catch、finally 三个代码块中变量的作用域分别独立而不能相
    互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。
    第三、多个catch 块时候,Java 虚拟机会匹配其中一个异常类或其子类,
    就执行这个catch 块,而不会再执行别的catch 块。
    第四、throw 语句后不允许有紧跟其他语句,因为这些没有机会执行。
    第五、如果一个方法调用了另外一个声明抛出异常的方法,那么这个方
    法要么处理异常,要么声明抛出。
    2.2throw 和throws 关键字的区别:
    throw 用来抛出一个异常,在方法体内。语法格式为:throw 异常对象。
    throws 用来声明方法可能会抛出什么异常,在方法名后,语法格式为:
    throws 异常类型1,异常类型2...异常类型n。
    :三: 下面列出可能发生空指针异常的几种情况及相应解决方案:

    代码段1:
      out.println(request.getParameter("username"));
      分析:代码段1的功能十分简单,就是输出用户输入"username"的值。
    说明:看上去,上面的语句找不出什么语法错误,而且在大多数情况下也遇不到什么问题。但是,如果某个用户在输入数据时并没有提供表单 域"username" 的值,或通过某种途径绕过表单直接输入时,此request.getParameter("username")的值为空(注意不是空字符串,是空对象 null。),out对象的println方法是无法直接对空对象操作的,因此代码段1所在的JSP页面将会抛出 "Java.lang.NullPointerException"异常。而且即使对象可能为空时,也调用Java.lang.Object或 Object对象本身的一些方法如toString(), equal(Object obj)等操作。

    代码段2:
      String userName = request.getParameter("username");
      If (userName.equals("root"))
      {....}
      分析:代码段2的功能是检测用户提供的用户名,如果是用户名称为"root"的用户时,就执行一些特别的操 作。

    说明:在代码段2中,如果有用户没有提供表单域"username"的值时,字符串对象userName为null值,不能够将一个null的对象与另一 个对象直接比较,同样,代码段2所在的JSP页面就会抛空指针错误。

    一个小技巧:如果要把某个方法的返回值与常量做比较,把常量放在前面,可以避免调用null对象的equals方法。譬如:

    If ("root".equals(userName))
      {....}

    即使userName对象返回了null对象,这里也不会有空指针异常,可以照常运转。

    代码段3:
      String userName = session.getAttribute("session.username").toString();
    分析:代码段3的功能是将session中session.username的值取出,并将该值赋给字符串对象userName。
    说明:在一般情况下,如果在用户已经进行某个会话,则不会出现什么问题;但是,如果此时应用服务器重新启动,而用户还没有重新登录,(也可能是用户关闭浏 览器,但是仍打开原来的页面。)那么,此时该session的值就会失效,同时导致session中的session.username的值为空。对一个 为 null的对象的直接执行toString()操作,就会导致系统抛出空指针异常。

    代码段4:
    public static void main(String args[]){

    Person p=null;

    p.setName("张三");

    System.out.println(p.getName());

    }

    分析:声明一个Person对象,并打印出该对象的中的Name名字。

    说明:这个时候你的p就出现空指针异常,因为你只是声明了这个Person类型的对象并没有创建对象,所以它的堆里面没有地址引用,切忌你要用对 象掉用方法的时候一定要创建对象。
      A:不管对象是否为空就直接开始使用。
      (JSP)代码段1:
      out.println(request.getParameter("username"));
      分析:代码段1的功能十分简单,就是输出用户输入"username"的值。
    说明:看上去,上面的语句找不出什么语法错误,而且在大多数情况下也遇不到什么问题。但是,如果某个用户在 输入数据时并没有提供表单域"username" 的值,或通过某种途径绕过表单直接输入时,此request.getParameter("username")的值为空(注意不是空字符串,是空对象 null。),out对象的println方法是无法直接对空对象操作的,因此代码段1所在的JSP页面将会抛出 "Java.lang.NullPointerException"异常。而且即使对象可能为空时,也调用Java.lang.Object或Object对象本身的一些方法如toString(), equal(Object obj)等操作。

      (JSP)代码段2:
      String userName = request.getParameter("username");
      If (userName.equals("root"))
      {....}
      分析:代码段2的功能是检测用户提供的用户名,如果是用户名称为"root"的用户时,就执行一些特别的操作。

    说明:在代码段2中,如果有用户没有提供表单域"username"的值时,字符串对象userName为null值,不能够将一个null的对象与另一个对象直接比较,同样,代码段2所在的JSP页面就会抛(Java.lang.NullPointerException)空指针错误。

      (JSP)代码段3:
      String userName = session.getAttribute
      ("session.username").toString();
    分析:代码段3的功能是将session中session.username的值取出,并将该值赋给字符串对象userName。
    说明:在一般情况下,如果在用户已经进行某个会话,则不会出现什么问题;但是,如果此时应用服务器重新启动 ,而用户还没有重新登录,(也可能是用户关闭浏览器,但是仍打开原来的页面。)那么,此时该session的值就会失效,同时导致session中的session.username的值为空。对一个为 null的对象的直接执行toString()操作,就会导致系统抛出(Java.lang.NullPointerException)空指针异常。

    展开全文
  • C语言中的空指针空指针常量、NULL & 0   本文转自:http://bbs.chinaunix.net/viewthread.php?tid=544415&extra=&page=7  帖子里讨论了C语言中的空指针空指针常量、NULL、0等概念及...
    
    
    



        帖子里讨论了C语言中的空指针、空指针常量、NULL、0等概念及相互关系及区别。这里摘录whyglinux兄的总结。做个标签,呵呵^_^

    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 (tyc: 我觉得(void*)0应该算是一个空指针吧,更恰当一点)等都是空指针常量(注意 (char*) 0 不叫空指针常量,只是一个空指针值)。至于系统选取哪种形式作为空指针常量使用,则是实现相关的。一般的 C 系统选择 (void*)0 或者 0 的居多(也有个别的选择 0L);至于 C++ 系统,由于存在严格的类型转化的要求,void* 不能象 C 中那样自由转换为其它指针类型,所以通常选 0 作为空指针常量(tyc: C++标准推荐),而不选择 (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 都成为一个空指针,由系统保证空指针不指向任何实际的对象或者函数。反过来说,任何对象或者函数的地址都不可能是空指针。(tyc: 比如这里的(void*)0就是一个空指针。把它理解为null pointer还是null pointer constant会有微秒的不同,当然也不是紧要了

    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 成为一个空指针。(tyc:很多系统中的实现:#define NULL (void*)0,与这里的“a null pointer constant”并不是完全一致的

    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”(空指针)。(tyc:一般系统中手册中都返回NULL,那我们就用NULL吧

    另外,附C FAQ上关于null pointer的解释:C FAQ:null pointer



     

    空指针:NULL还是0

    看林锐博士的《高质量C/CPP编程》附录的试卷,对空指针的判断居然强制要用NULL(如 if(p==NULL) ),后来从这篇文章看到一些东西觉得有点意思。不耐烦看的人看我的归纳: 

    0、0和数值“零”在指针上下文中不是一回事,0就是空指针,而不一定是“零” 
    1、用0还是NULL表示空指针是风格问题,而不是对与错的问题。 
    2、空指针真的有非零的,多是罕见机器。但此时 0 在指针上下文中会自动转为合适的空指针。 
    3、用 if(p), if(!p) 还是 if(p!=NULL), if(p==NULL) 都完全合法。 
    4、NULL一般被定义为0或(void*)0 
    5、0作为函数实参时,为了表示它是空指针,最好把它至于指针上下文中,即加上(char*)或(void*)修饰。(这要看编译器了,我在gcc4.1下就不需要修饰)。 

    ================================================= 
    原文出处:http://c-faq-chn.sourceforge.net/ccfaq/ 
    部分文摘 
    ================================================= 
    6.2 怎样在程序里获得一个空指针? 
    根据语言定义, 在指针上下文中的常数 0 会在编译时转换为空指针。也就是说, 在初始化、赋值或比较的时候, 如果一边是指针类型的值或表达式, 编译器可以确定另一边的常数 0 为空指针并生成正确的空指针值。因此下边的代码段完全合法: 

    char *p = 0; 
    if(p != 0) 

    参见问题 5.3。 

    然而, 传入函数的参数不一定被当作指针环境, 因而编译器可能不能识别未加修饰的 0 ``表示" 指针。在函数调用的上下文中生成空指针需要明确的类型转换, 强制把 0 看作指针。例如, Unix 系统调用 execl 接受变长的以空指针结束的字符指针参数。它应该如下正确调用: 

    execl("/bin/sh", "sh", "-c", "date", (char *)0); 

    如果省略最后一个参数的 (char *) 转换, 则编译器无从知道这是一个空指针, 从而当作一个 0 传入。(注意很多 Unix 手册在这个例子上都弄错了。) 

    如果范围内有函数原型, 则参数传递变为 “赋值上下文", 从而可以安全省略多数类型转换, 因为原型告知编译器需要指针, 使之把未加修饰的 0 正确转换为适当的指针。函数原型不能为变长参数列表中的可变参数提供类型。 (参见问题 15.3) 在函数调用时对所有的空指针进行类型转换可能是预防可变参数和无原型函数出问题的最安全的办法。 


    ============================ 
    6.3 用缩写的指针比较 "if(p)" 检查空指针是否可靠?如果空指针的内部表达不是 0 会怎么样? 
    当 C 在表达式中要求布尔值时, 如果表达式等于 0 则认为该值为假, 否则为真。换言之, 只要写出 

    if(expr) 

    无论"expr" 是任何表达式, 编译器本质上都会把它当 

    if((expr) != 0) 

    处理。 

    如果用指针 p 代替 "expr" 则 

    if(p) 等价于 if(p != 0)。 

    而这是一个比较上下文, 因此编译器可以看出 0 实际上是一个空指针常数, 并使用正确的空指针值。这里没有任何欺骗; 编译器就是这样工作的, 并为、二者生成完全一样的代码。空指针的内部表达无关紧要。 

    布尔否操作符 ! 可如下描述: 
    !expr 本质上等价于 (expr)?0:1 
    或等价于 ((expr) == 0) 
    从而得出结论 
    if(!p) 等价于 if(p == 0) 
    类似 if(p) 这样的 "缩写", 尽管完全合法, 但被一些人认为是不好的风格 (另外一些人认为恰恰是好的风格; 参见问题 17.8)。 

    参见问题 9.2。 

    ============================ 
    6.4 NULL 是什么, 它是怎么定义的? 
    作为一种风格, 很多人不愿意在程序中到处出现未加修饰的 0。因此定义了预处理宏 NULL (在 <stdio.h> 和其它几个头文件中) 为空指针常数, 通常是 0 或者 ((void *)0) (参见问题 5.6)。希望区别整数 0 和空指针 0 的人可以在需要空指针的地方使用 NULL。 

    使用 NULL 只是一种风格习惯; 预处理器把所有的 NULL 都还原回 0, 而编译还是依照 上文的描述处理指针上下文的 0。特别是, 在函数调用的参数里, NULL 之前 (正如在 0 之前) 的类型转换还是需要。问题 5.2 下的表格对 0 和 NULL 都有效 (带修饰的 NULL 和带修饰的 0 完全等价)。 

    NULL 只能用作指针常数; 参见问题 5.7。 

    ============================ 
    6.14 说真的, 真有机器用非零空指针吗, 或者不同类型用不同的表达? 
    至少 PL/I, Prime 50 系列用段 07777, 偏移 0 作为空指针。后来的型号使用段 0, 偏移 0 作为 C 的空指针, 迫使类似 TCNP (测试 C 空指针) 的指令明显地成了现成的作出错误猜想的蹩脚 C 代码。旧些的按字寻址的 Prime 机器同样因为要求字节指针 (char *) 比字指针 (int *) 长而臭名昭著。 

    Data General 的 Eclipse MV 系列支持三种结构的指针格式 (字、字节和比特指针), C 编译器使用了其中之二:char * 和 void * 使用字节指针, 而其它的使用字指针。 

    某些 Honeywell-Bull 大型机使用比特模式 06000 作为 (内部的) 空指针。 

    CDC Cyber 180 系列使用包含环 (ring), 段和位移的 48 位指针。多数用户 (在环 11 上) 使用的空指针为 0xB00000000000。 在旧的 1 次补码的 CDC 机器上用全 1 表示各种数据, 包括非法指针, 是十分常见的事情。 

    旧的 HP 3000 系列对字节地址和字地址使用不同的寻址模式; 正如上面的机器一样, 它因此也使用不同的形式表达 char * 和 void * 型指针及其它指针。 

    Symbolics Lisp 机器是一种标签结构, 它甚至没有传统的数字指针; 它使用 <NIL, 0> 对 (通常是不存在的 <对象, 偏移> 句柄) 作为 C 空指针。 

    根据使用的 ``内存模式", 8086 系列处理器 (PC 兼容机) 可能使用 16 位的数据指针和 32 位的函数指针, 或者相反。 

    一些 64 位的 Cray 机器在一个字的低 48 位表示 int *; char * 使用高 16 位的某些位表示一个字节在一个字中的偏移。 


    ============================ 
    6.7 如果 NULL 和 0 作为空指针常数是等价的, 那我到底该用哪一个呢? 
    许多程序员认为在所有的指针上下文中都应该使用 NULL, 以表明该值应该被看作指针。另一些人则认为用一个宏来定义 0, 只不过把事情搞得更复杂, 反而令人困惑。因而倾向于使用未加修饰的 0。没有正确的答案。 (参见问题 9.2 和 17.8) C 程序员应该明白, 在指针上下文中 NULL 和 0 是完全等价的, 而未加修饰的 0 也完全可以接受。任何使用 NULL (跟 0 相对) 的地方都应该看作一种温和的提示, 是在使用指针; 程序员 (和编译器都) 不能依靠它来区别指针 0 和整数 0。 

    在需要其它类型的 0 的时候, 即便它可能工作也不能使用 NULL, 因为这样做发出了错误的格式信息。(而且, ANSI 允许把 NULL 定义为 ((void *)0), 这在非指针的上下文中完全无效。特别是, 不能在需要 ASCII 空字符 (NUL) 的地方用 NULL。如果有必要, 提供你自己的定义 

    #define NUL '\0'


    展开全文
  • 关于空指针

    2018-06-14 22:34:30
    臭名昭著的空指针到底是什么?... 永远也不能得到空指针, 同样对 malloc() 的成功调用也不会返回空指针, 如果失败, malloc() 的确返回空指针, 这是空指针的典型用法: 表示 "未分配" 或者...
    臭名昭著的空指针到底是什么?
     
    语言定义中说明, 每一种指针类型都有一个特殊值 --- "空指针" ---  它与同类型的其它所有指针值都不相同, 它 "与任何对象或函数的指针值都不相等"。也就是说, 取地址操作符 & 永远也不能得到空指针, 同样对 malloc() 的成功调用也不会返回空指针, 如果失败, malloc() 的确返回空指针, 这是空指针的典型用法: 表示 "未分配" 或者 "尚未指向任何地方" 的指针。

    空指针在概念上不同于未初始化的指针。空指针可以确保不指向任何对象或函数; 而未初始化指针则可能指向任何地方。

    如上所述, 每种指针类型都有一个空指针, 而不同类型的空指针的内部表示可能不尽相同。尽管程序员不必知道内部值, 但编译器必须时刻明确需要那种空指针, 以便在需要的时候加以区分

    怎样在程序里获得一个空指针?
     
    根据语言定义, 在指针上下文中的常数 0 会在编译时转换为空指针。也就是说, 在初始化、赋值或比较的时候, 如果一边是指针类型的值或表达式, 编译器可以确定另一边的常数 0 为空指针并生成正确的空指针值。因此下边的代码段完全合法:
        char *p = 0;
        if(p != 0)
    
     

    然而, 传入函数的参数不一定被当作指针环境, 因而编译器可能不能识别未加修饰的 0 “表示" 指针。在函数调用的上下文中生成空指针需要明确的类型转换, 强制把 0 看作指针。例如, Unix 系统调用 execl 接受变长的以空指针结束的字符指针参数。它应该如下正确调用:

        execl("/bin/sh", "sh", "-c", "date", (char *)0);
    

    如果省略最后一个参数的 (char *) 转换, 则编译器无从知道这是一个空指针, 从而当作一个 0 传入。(注意很多 Unix 手册在这个例子上都弄错了。)

    如果范围内有函数原型, 则参数传递变为 "赋值上下文", 从而可以安全省略多数类型转换, 因为原型告知编译器需要指针, 使之把未加修饰的 0  正确转换为适当的指针。函数原型不能为变长参数列表中的可变参数提供类型。 

     在函数调用时对所有的空指针进行类型转换可能是预防可变参数和无原型函数出问题的最安全的办法。

    摘要:

    可以使用未加修饰的 0:需要显示的类型转换:
    初始化函数调用, 作用域内无原型
    赋值变参函数调用中的可变参数
    比较 
    固定参数的函数调用且在作用域内有原型 

     

    用缩写的指针比较 "if(p)" 检查空指针是否可靠?

     

    如果空指针的内部表达不是 0 会怎么样? 当 C 在表达式中要求布尔值时, 如果表达式等于 0 则认为该值为假, 否则为真。换言之, 只要写出

        if(expr)
    

    无论 "expr" 是任何表达式, 编译器本质上都会把它当

        if((expr) != 0)
    

    处理。

    如果用指针 p 代替 "expr" 则

        if(p) 等价于 if(p != 0)。
    

    而这是一个比较上下文, 因此编译器可以看出 0 实际上是一个空指针常数, 并使用正确的空指针值。这里没有任何欺骗; 编译器就是这样工作的, 并为、二者生成完全一样的代码。空指针的内部表达无关紧要

    布尔否操作符 ! 可如下描述:

    !expr本质上等价于(expr)?0:1
     或等价于((expr) == 0)

    从而得出结论

    if(!p)等价于if(p == 0)

    类似 if(p) 这样的 "缩写", 尽管完全合法, 但被一些人认为是不好的风格

    NULL 是什么, 它是怎么定义的?

    作为一种风格, 很多人不愿意在程序中到处出现未加修饰的 0。因此定义了预处理宏  NULL (在 <stdio.h> 和其它几个头文件中) 为空指针常数, 通常是 0 或者 ((void *)0) 。希望区别整数 0 和空指针 0  的人可以在需要空指针的地方使用 NULL。

    使用 NULL 只是一种风格习惯; 预处理器把所有的 NULL 都还原回 0, 而编译还是依照 上文的描述处理指针上下文的 0。特别是, 在函数调用的参数里, NULL 之前 (正如在 0  之前) 的类型转换还是需要。

    摘要:

    可以使用未加修饰的 0:需要显示的类型转换:
    初始化函数调用, 作用域内无原型
    赋值变参函数调用中的可变参数
    比较 
    固定参数的函数调用且在作用域内有原型 

    的表格对 0 和 NULL 都有效 (带修饰的  NULL 和带修饰的 0 完全等价)。

    NULL 只能用作指针常数;

    在使用非全零作为空指针内部表达的机器上, NULL 是如何定义的?

     跟其它机器一样: 定义为 0 (或某种形式的 0;

    当程序员请求一个空指针时, 无论写 "0" 还是 "NULL", 都是有编译器来生成适合机器的空指针的二进制表达形式。因此, 在空指针的内部表达不为 0 的机器上定义 NULL 为 0 跟在其它机器上一样合法:编译器在指针上下文看到的未加修饰的 0 都会被生成正确的空指针。

    如果 NULL 定义成 #define NULL ((char *)0)  难道不就可以向函数传入不加转换的 NULL 了吗?

    一般情况下, 不行。复杂之处在于, 有的机器不同类型数据的指针有不同的内部表达。这样的 NULL 定义对于接受字符指针的的函数没有问题, 但对于其它类型的指针参数仍然有问题 (在缺少原型的情况下), 而合法的构造如

        FILE *fp = NULL;
    

    则会失败。

    不过, ANSI C 允许 NULL 的可选定义

        #define NULL ((void *)0)
    

    除了潜在地帮助错误程序运行 (仅限于使用同样类型指针的机器, 因此帮助有限) 以外, 这样的定义还可以发现错误使用 NULL 的程序 (例如, 在实际需要使用 ASCII NUL 字符的地方)。

    无论如何, ANSI 函数原型确保大多数 (尽管不是全部; )指针参 数在传入函数时正确转换。 因此, 这个问题有些多余。

    如果 NULL 和 0 作为空指针常数是等价的, 那我到底该用哪一个呢?

    许多程序员认为在所有的指针上下文中都应该使用 NULL, 以表明该值应该被看作指针。另一些人则认为用一个宏来定义 0, 只不过把事情搞得更复杂, 反而令人困惑。因而倾向于使用未加修饰的 0。没有正确的答案。 

     C 程序员应该明白, 在指针上下文中  NULL 和 0 是完全等价的, 而未加修饰的 0 也完全可以接受。任何使用 NULL  (跟 0 相对) 的地方都应该看作一种温和的提示, 是在使用指针; 程序员 (和编译器都) 不能依靠它来区别指针 0 和整数 0。

    在需要其它类型的 0 的时候, 即便它可能工作也不能使用 NULL, 因为这样做发出了错误的格式信息。(而且, ANSI 允许把 NULL 定义为 ((void *)0), 这在非指针的上下文中完全无效。特别是, 不能在需要 ASCII 空字符 (NUL) 的地方用 NULL。如果有必要, 提供你自己的定义

        #define NUL '/0'
    

     

    但是如果 NULL 的值改变了, 比如在使用非零内部空指针的机器上, 难道用 NULL (而不是 0) 不是更好吗?

    不。(用 NULL 可能更好, 但不是这个原因。) 尽管符号常量经常代替数字使用以备 数字的改变, 但这不是用 NULL 代替 0 的原因。语言本身确保了源码中的 0 (用于指针上下文) 会生成空指针。NULL 只是用作一种格式习惯。

     

    这有点奇怪。NULL 可以确保是 0, 但空 (null) 指针却不一定?

    随便使用术语 "null" 或 "NULL" 时, 可能意味着以下一种或几种含义:

    1. 概念上的空指针, 定义的抽象语言概念。它使用以下的东西实现的 ……
    2. 空指针的内部 (或运行期) 表达形式, 这可能并不是全零, 而且对不用的指针类型可能不一样。真正的值只有编译器开发者才关心。C 程序的作者永远看不到它们, 因为他们使用 ……
    3. 空指针常数, 这是一个常整数 0 。它通常隐藏在  ……
    4. NULL 宏, 它被定义为 0 ()。最后转移我们注意力到  ……
    5. ASCII 空字符 (NUL), 它的确是全零, 但它和空指针除了在名称上以外, 没有任何必然关系; 而……
    6. "空串" (null string), 它是内容为空的字符串 ("")。在 C 中使用空串这个术语可能令人困惑, 因为空串包括空字符 ('/0'),但 包括空指针, 这让我们绕了一个完整的圈子 ……

    本文用短语 ``空指针" (``null pointer", 小写) 表示第一种含义, 标识 ``0"  或短语 ``空指针常数" 表示含义 3, 用大写 NULL 表示含义 4。  

    展开全文
  • C++ 空指针和野指针

    万次阅读 2020-06-21 20:50:05
    //1,指针变量指向内存中编号为0的空间为空指针 //2,空指针指向的内存空间是不可以访问的 #include<iostream> using namespace std; int main() { int a = 10; int * p = &a; cout << p &...
  • 避免空指针的最根本办法

    千次阅读 2018-10-10 19:47:52
    空指针异常可以说是新人新手最容易犯的错误了,解决起来也不难:仔细看控制台空指针报错的地址,去检查数据源就行。 如果不要要空指针出现,那么我们在写代码的时候,就一定要对(可能)为空的数据进行判断: 比如...
  • Java应用中抛出的空指针异常是解决空指针的最好方式,也是写出能顺利工作的健壮程序的关键。 避免Java中的空指针异常的常用技巧(同时避免大量的非空检查): 1) 从已知的String对象中调用equals()和...
  • 空指针

    2020-02-10 21:35:05
    空指针 空指针:它“保证与任何对象或函数的指针值都不相等”。也就是说,空指针不会指向任何地方,它不是任何对象或函数的地址。每种指针类型都有一个空指针,而不同类型的空指针的内部表示可能不尽相同。取地址操作符&...
  • 空指针->无效指针

    2019-04-21 10:18:53
    对一个空指针进行解引用是错误的,会导致程序崩溃,例如下面的代码: int main() { int a = 10; int *p = &a; p = NULL;//NULL:空指针,是唯一一个表示无效指针 printf("%d\n",*p);//崩溃是正常行为 printf...
  • Java 空指针异常的若干解决方案

    万次阅读 2018-09-22 21:04:03
    Java 中任何对象都有可能为空,当我们调用空对象的方法时就会抛出 NullPointerException 空指针异常,这是一种非常常见的错误类型。我们可以使用若干种方法来避免产生这类异常,使得我们的代码更为健壮。本文将列举...
  • 在开发中,需要传入页面表单作为查询条件,在DAO层进行数据库的select操作,如果form为,那么查询所有的数据出来,如果form不为,那么查询满足form条件的数据。我们把这个开发需求形象化一点:一个学生登录教务...
  • 空指针空指针:就是一个被赋值为0的指针,它不指向任何的对象或者函数。1、使用指针的第一件事就是需要看这个指针是否是空指针(坚决不能使用空指针,否则程序就会蹦。意思就是:为一个指针赋值为空指针是不会报错的...
  • mapToLong报空指针异常

    万次阅读 2020-06-28 17:25:07
    mapToLong报空指针异常 Exception in thread "main" java.lang.NullPointerException at test.collection.TestAuto.lambda$null$0(TestAuto.java:12) at java.util.stream.ReferencePipeline$5$1.accept...
  • C语言空指针

    千次阅读 2013-07-26 13:55:36
    也就是说, 取地址操作符 & 永远也不能得到空指针, 同样对 malloc() 的成功调用也不会返回空指针, 如果失败, malloc() 的确返回空指针, 这是空指针的典型用法:表示 “未分配”或者 “尚未指向任何地方”的指针。...
  • 【C++进阶】C++中的空指针和野指针

    万次阅读 多人点赞 2014-03-12 23:43:27
    空指针常量,空指针空指针赋值区以及野指针及其形成原因
  • Android 防止空指针异常

    千次阅读 2019-07-29 10:23:40
    文章目录空指针异常防止空指针异常Java 注解Java 8 中的 Optional 类型Kotlin总结 空指针异常 先复习下异常。 异常分为 Exception 和 Error,Exception 和 Error 类都继承自Throwable类。 Exception(程序可恢复)...
  • java空指针异常:预防及解决办法

    千次阅读 2020-02-11 11:26:27
    这是我16年在博客园发的一篇博客,彼时还在学校。...空指针就是空引用,java空指针异常就是引用本身为空,却调用了方法,这个时候就会出现空指针异常。可以理解,成员变量和方法是属于对象的(除...
  • 空指针的深入理解(C/C++)

    千次阅读 2014-05-13 10:31:23
    空指针究竟指向了内存的哪个地方? 如果仅仅声明一个指针,而没有任何赋值,那么这个指针是野指针,它会指到VM的任何位置,碰到异常操作,比如对只读区写操作,就会引起硬件中断产生core,也就是通常的段错误。 ...
  • 空指针常量 一个表示0值的整数常量,叫做空指针常量。例如:0、0L、1-1(它们都是值为0的整数常量表达式)以及(void*)0、void* NULL 都是空指针常量,空指针常量可以赋值给任何指针类型,因为它是变体类型(void*...
  • 空指针、NULL指针、零指针

    千次阅读 2013-09-02 14:39:20
    0、0L、'\0'、3 - 3、0 * 17 (它们都是“integer constant expression”)以及 (void*)0 (我觉得(void*)0应该算是一个空指针吧,更恰当一点)等都是空指针常量(注意 (char*) 0 不叫空指针常量,只是一个空指针值...

空空如也

1 2 3 4 5 ... 20
收藏数 622,320
精华内容 248,928
关键字:

空指针