美文网首页
Java、OC、C/C++中的null

Java、OC、C/C++中的null

作者: 康小曹 | 来源:发表于2021-02-22 19:48 被阅读0次

原由

Java 中的代码:

Object nullObj = null;
System.out.println(nullObj.toString());

此时就会出现注明的空指针异常,可是 OC 中的 nil 并不会如此。其原因是汇编方法 msg_send 处理了消息调用者为 nil 时的情况,大概处理如下:

LNilReceiver:
    // r0 is already zero
    mov r1, #0
    mov r2, #0
    mov r3, #0
    FP_RETURN_ZERO
    bx  lr  

    END_ENTRY _objc_msgSend

那么,问题来了:

  1. OC 中的 nil 是什么?
  2. 为什么 Java 中会崩溃?
  3. C/C++ 中 null 会崩溃吗?

一、OC 中的空

OC 中可以使用 4 种方式来代表空:

  1. nil;
  2. Nil;
  3. NULL;
  4. NSNull;

OC 中没有 null;

查阅 objc4 源码可知,

nil 的声明:

#ifndef nil
# if __has_feature(cxx_nullptr)
#   define nil nullptr
# else
#   define nil __DARWIN_NULL
# endif
#endif

Nil 的声明:

#ifndef Nil
# if __has_feature(cxx_nullptr)
#   define Nil nullptr
# else
#   define Nil __DARWIN_NULL
# endif
#endif

通过源码可知,其实 Nil 完全等价于 nil,要弄清楚本质,关键点有三个:

  1. __has_feature(cxx_nullptr);
  2. nullptr;
  3. __DARWIN_NULL

关键点1:__has_feature

根据Clang 3.7 文档对__has_feature的描述:

__has_feature evaluates to 1 if the feature is both supported by Clang and standardized in the current language standard or 0 if not.

因此,__has_feature(cxx_nullptr) 是用来判断是否支持 C++11 中的 nullptr 特性的。所以,在 Objective-C 中 nil 和 Nil 都是 __DARWIN_NULL 宏定义。

其实,验证这一点也很简单,在 iOS 工程中运行以下代码即可:

# if __has_feature(cxx_nullptr)
#   define xk_test 0
# else
#   define xk_test 1
# endif

NSLog(@"%d",xk_test); // 输出为1,表示iOS中不支持cxx_nullptr

那么来看看__DARWIN_NULL 是个啥,仍然在源码中找到:

#ifndef __DARWIN_NULL
#define __DARWIN_NULL NULL
#endif

因此:

  • iOS 中,Nil、nil、NULL 完全等价;

另外,NSNull 就是一个对象,只有一个 [NSNull null] 方法,就不多说了吧~~~

二、C++ 和 C 中的 NULL

既然 OC 中的 nil、Nil 都是 NULL,那么 NULL 到底是个啥?

NULL 是 C/C++ 中定义的宏,但是两者却不是完全等价的,在 VC 的 runtime.h 中,其定义如下:

#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif

由此可知:

  1. C 中的 NULL 为 (void *) 0;
  2. C++ 中的 NULL 就是 0;

其本质区别就是 C 中将 0 强制转化成了一个空指针;

那为什么在 C++ 中会直接定义 NULL 为 0 呢?因为 C 中可以隐式转换,如:

int a = (void *)0;

而 C++ 却是需要显示的写出类型转换的,如:

int *p = (void *) 0; // error

Xcode 中,cpp 文件下,报错如下:

C++没有隐式转换

.m 文件中转换只会报警告:


C中可以隐式转换

所以,NULL 在 C++ 中直接定义为 0;

注意:.m 文件和 .cpp 文件的说法并不准确,是否编译通过取决于编译器的设置。可以使用 ifdefine 来测试当前编译器是否支持 __cplusplus;

三、C++ 中的NULL 延伸

因为 C++ 中,NULL 定义为 0,那么在函数重载时,使用 gcc 编译器运行时,就会发生偏差,NULL 本来是代表空指针的,却调用到 int 类型的函数:

#include <iostream>

using namespace std;

void func(int a)
{
    cout << "func int" << endl;
}

void func(char* a)
{
    cout << "func char*" << endl;
}
int main()
{
    func(NULL);
    return 0;
}

输出:

func int

使用 C++ 编译器编译时,C++ 会直接禁止使用 NULL 作为重载函数的入参,会直接报错:

NULL作为重载函数入参

另外,需要特殊说明的一点是:

  • NULL 在 C++ 中严格来讲是 long 类型;

起初,觉得 NULL 既然是 0,那么:

int *a = NULL;
*a = 10;

一开始觉得这种写法是不对的,因为将整数赋值给了指针。但是其实这么写也正确,因为指针接收一个地址,地址本质上就是一个表示内存位置的数字。只不过被赋值为 NULL ,指针的地址为 0 而已。因此,继续使用 a 这个指针就会出现坏内存访问( EXC_BAD_ACCESS)。这也就解释了第三个问题:同 Java 一样,使用空指针在 C/C++ 中会崩溃;

另外,还想更具体看 NULL 到底是个啥类型,作进一步验证。想到了使用 type of 方法:

printf("NULL:%lu\n",sizeof(typeof(NULL)));

但是结果确是 8,难道 C++ 后面的版本 NULL 被改成了 void * ?

于是继续对比:

printf("NULL:%lu\n",sizeof(typeof(NULL)));
printf("nullptr:%lu\n",sizeof(typeof(nullptr)));

输出为:

8
8

这不对啊,解释不通啊。后来想到,Java 中的字面量具有默认类型的特性,难道 C++ 中 0 字面量的类型为 long 类型:

printf("int:%lu\n",sizeof(typeof(0)));

输出为:

4

那么就只有一个解释了:

  • NULL = 0L;

验证:

if(typeid(NULL) == typeid(int)){
    cout << "NULL的数据类型是:int型" << endl;
}

if(typeid(NULL) == typeid(long)){
    cout << "NULL的数据类型是:long型" << endl;
}

if(typeid(NULL) == typeid(void *)){
    cout << "NULL的数据类型是:指针型" << endl;
}

结果:

NULL的数据类型是:long型

由此证明:

*当前版本 C++ 中的 null 并不是 int 类型的 0,而是 long 类型的 0;

四、nullptr

因为 NULL 的定义不够清晰,所以推出了 nullptr 来代指空指针。如果编译器支持 nullptr,理论上不应该再使用 NULL 来给空指针赋值或者清空指针,而应该使用 nullptr,而给 int 类型赋值则直接使用 0 即可。

将 nullptr 赋值给非指针类型,则直接报错:


nullptr
  • nullptr 本质上是一个编译器特性,是为了代码的书写规范而产生,让空指针的赋值更加清晰;

五、Java 中的 null

起初,因为 OC 中习惯了消息转发机制,在调用者为 nil 时,消息转发机制不会做任何事情,所以使用空指针并不会引起崩溃。因此,不理解 java 中的 null 为什么直接奔溃,其实 C++ 本身就是不可以使用空指针的,只是 OC 做了特殊处理罢了:

int i = NULL;
i = 10;

int *a = NULL;
*a = 1; // EXC_BAD_ACCESS 

除此之外,Java 中的 null 具备几个特点:

  1. null 是一个关键字,不是编译器特性;
  2. null 不是一种类型,而是一个值;
  3. null 是所有引用类型的默认值;
  4. null 不能赋值给基础数据类型;
  5. 包装类(Integer)在自动拆箱(Integer赋值给Int)时,如果为 null ,不会自动转换成对应基础类型的默认值,而是会报空指针异常引起崩溃;
Integer integer1 = 10;
int int1 = integer1;

Integer integer2 = null;
int int2 = integer2; // NullPointerException
  1. null 调用 instanceof 方法永远返回 false;
Object obj = null;
if (obj instanceof Object){
    System.out.println("True");
} else {
    System.out.println("False");
}
  1. null == null 返回 true 可以使用 == 和 != 来判断是否为 null;
Object obj = null;
if (obj == null){
    System.out.println("True");
} else {
    System.out.println("False");
}
  1. null 是值而不是类型,所以不能当做 instanceof 的参数;


    null不是一种类型
  2. 不能使用值为 null 的引用类型变量来调用非静态方法;

  3. 可以使用值为 null 的引用类型变量来调用静态方法;

class XKPerson {

    static final String COUNTRY = "CHINA";

    static public void StaticFunc(){
        System.out.println("123");
    }

    public void InstanceFunc(){
        System.out.println("456");
    }
}

public class Main {
    public static void main(String[] args) {
        XKPerson p1 = null;
        p1.StaticFunc(); // correct
        p1.InstanceFunc(); // error, NullPointerException
    }
}

静态方法:实例对象可以调用静态方法,也可以访问静态成员变量,但是静态方法的调用不需要实例对象,所以对象为 null 时调用静态方法正常执行,调用实例方法则会空指针异常;

  1. null 可以作为参数传递给方法,但是否崩溃取决于方法内部的处理;

六、总结

  1. OC 中的 Nil、nil、NULL 完全等价;
  2. C++ 不支持隐式类型转换而 C 支持,所以 C++ 中对 NULL 的定义做了特殊处理,直接定义为 0;
  3. C++ 支持方法重载,NULL 在 GCC 编译器中会对重载方法的调用产生误差,调用到 int 类型,在 C++ 编译器中直接报错;
  4. 因为 NULL 的定义不是很清晰所以出现了 nullptr 代替 NULL,表示空指针,nullptr 用于指针类型,赋值给非指针类型时直接编译期报错;
  5. NULL 虽然是 0,但是是 long 类型;
  6. Java 中的 null 是一种特殊的值,表示引用类型的指针为空;

相关文章

  • Java、OC、C/C++中的null

    原由 Java 中的代码: 此时就会出现注明的空指针异常,可是 OC 中的 nil 并不会如此。其原因是汇编方法 ...

  • 关于null值比较

    一 null值比较场景 对于C/C++或者java中对于null值的比较,有的时候直接使用if(!p)有的时候使用...

  • OBJECT-C的特点以及跟其它语言的对比

    消息 消息是OC的主要特征。给对象发送消息 等于是 java/c++中调用类对象的函数 在C++或Java里,...

  • Objective-C的空

    nil:OC为空 Nil:OC的类为空 NULL:c/c++的空指针 NSNull:对nil的包装 可以存储到数组...

  • FFmpeg视频播放

    首先记录一下C++中的NULL、0、nullptr的区别 NULL在C++中就是0,这是因为在C++中void* ...

  • 知识点(一)

    语言(C++/C/Java/Python/Shell/JS/OC/Luna/Matlab) CS设计

  • C++工程:一文看懂如何使用 C++ 开发 Android、iO

    为什么使用C++ C/C++是相对底层的语言,相比OC、Swift、Kotlin、Java等都要难,但是C/C++...

  • 语言 函数

    函数如何定义: C: C++: OC: python: JavaScript: PHP: java: 为什么有的有...

  • Cycript使用及高级用法

    Cycript支持OC,C++,Java语法,笔者直接使用Monkey集成(自带的),可以找到monkey中相关的...

  • 己亥年-第一篇随笔

    谈一谈null在C和C++中的区别: C中NULL是指向0地址的指针常量(void *)0 C++中则是定义为整数...

网友评论

      本文标题:Java、OC、C/C++中的null

      本文链接:https://www.haomeiwen.com/subject/hsdxfltx.html