原由

Java 中的代码:

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

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

LNilReceiver:// r0 is already zeromov    r1, #0mov   r2, #0mov   r3, #0FP_RETURN_ZERObx  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
# endifNSLog(@"%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 文件下,报错如下:

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

所以,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 在 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 本质上是一个编译器特性,是为了代码的书写规范而产生,让空指针的赋值更加清晰;

五、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 的参数;

  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(); // correctp1.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相关推荐

  1. java json 去除空_详解Java去除json数据中的null空值问题

    1.描述 @JsonInclude(JsonInclude.Include.NON_NULL)标记是jackson包提供的json序列化方法,已经集成于Springboot2.0中,此方法的配置意在可 ...

  2. hutol json null值没了_详解Java去除json数据中的null空值问题

    1.描述 @JsonInclude(JsonInclude.Include.NON_NULL)标记是jackson包提供的json序列化方法,已经集成于Springboot2.0中,此方法的配置意在可 ...

  3. 将JAVA bean/实体类 中为null的属性值转换成空字符串

    使bean中为null的属性转换成空字符串 获得getter方法 方法有分带参数和不带参数,我们知道getter方法是不带参数的 获得getter方法如下 Method m = model.getCl ...

  4. java 静态类设置null_JAVA中对null进行强制类型转换(null可以强转为任意对象,并执行对象的静态方法)...

    今天很好奇,对null进行强转会不会抛错.做了如下测试得到的结果是, 如果把null强转给对象,是不会抛异常的,本身对象是可以为null的. 但是如果是基本类型,比如 int i = (Integer ...

  5. Java中有关Null的9件事

    对于Java程序员来说,null是令人头痛的东西.时常会受到空指针异常(NPE)的骚扰.连Java的发明者都承认这是他的一项巨大失误.Java为什么要保留null呢?null出现有一段时间了,并且我认 ...

  6. java8避免null_在 Java 8 中避免 Null 检查

    如何预防 Java 中著名的 NullPointerException 异常?这是每个 Java 初学者迟早会问到的关键问题之一.而且中级和高级程序员也在时时刻刻规避这个错误.其是迄今为止 Java ...

  7. java 返回空数组_避免在Java中检查Null语句

    1.概述 通常,在Java代码中处理null变量.引用和集合很棘手.它们不仅难以识别,而且处理起来也很复杂.事实上,在编译时无法识别处理null的任何错误,会导致运行时NullPointerExcep ...

  8. java中的null类型---有关null的9件事

    摘自 https://blog.csdn.net/qq_25077777/article/details/80174763 今天听到一个问题,java中的null类型,null竟然是一种类型 java ...

  9. java 类 null_深入理解java中的null“类型”

    本文研究的主要是java中的null"类型"的相关实例,具体介绍如下. 先给出一道简单的null相关的题目,引发我们对null的探讨,后面会根据官方语言手册对null"类 ...

  10. Java中的null是什么?

    As we know null is an important concept in every language not only in Java but here we will study va ...

最新文章

  1. MySQL DBA基本知识点梳理和查询优化
  2. Dataset:(公交车、恐龙、大象、花朵、骏马)六类图片数据集(AutoKeras测试)的简介、下载、使用方法之详细攻略
  3. LeetCode 49字母异位词分组50pow(x,n)51八皇后
  4. php5.6的apaches的dll_Windows 10下 搭建Apache2.4、php5.6、mysql5.6
  5. LeetCode176 第二高的薪水
  6. UVA - 1314 Hidden Password(最小表示法)
  7. MATLAB字符转数据
  8. 数据库性能自动压测-Oracle swingbench篇
  9. .Net学习(二):vb.net总结之似曾相识
  10. POJ-2488 A Knights Journey-深度优先搜索DFS
  11. C语言查看tuxedo队列长度,tuxedo的常用启、停、参数讲解、日志查看
  12. 2.3全卷积网络(FCN)与图像分割
  13. 32.从1到n整数中1出现的次数
  14. LINUX下载编译libpng
  15. IEEE论文模板下载地址及说明
  16. 康托尔点集matlab实数,为什么康托尔集内的数和实数个数一样多
  17. Mybatis的一级缓存和二级缓存,看完不再懵逼
  18. 离职员工删库跑路,3个方法找回数据,有备无患
  19. PS 滤镜算法原理 ——马赛克
  20. css 文本、文字展开与收缩,查看更多收起

热门文章

  1. 并发编程学习之CopyOnWriteArraySet
  2. Java虚拟机内存管理
  3. 《轻松读懂spring》之 IOC的主干流程(上)
  4. python模拟ssh登录
  5. wget下载网络图片
  6. java_math_BigInteger
  7. openmeeting开发心得及相关文档
  8. Spring 4.xx开发环境搭建
  9. vue获取子组件元素
  10. js事件循环机制-宏任务微任务