Java、OC、C/C++中的null
原由
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
那么,问题来了:
- OC 中的 nil 是什么?
- 为什么 Java 中会崩溃?
- C/C++ 中 null 会崩溃吗?
一、OC 中的空
OC 中可以使用 4 种方式来代表空:
- nil;
- Nil;
- NULL;
- 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,要弄清楚本质,关键点有三个:
- __has_feature(cxx_nullptr);
- nullptr;
- __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
由此可知:
- C 中的 NULL 为 (void *) 0;
- 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 具备几个特点:
- null 是一个关键字,不是编译器特性;
- null 不是一种类型,而是一个值;
- null 是所有引用类型的默认值;
- null 不能赋值给基础数据类型;
- 包装类(Integer)在自动拆箱(Integer赋值给Int)时,如果为 null ,不会自动转换成对应基础类型的默认值,而是会报空指针异常引起崩溃;
Integer integer1 = 10;
int int1 = integer1;Integer integer2 = null;
int int2 = integer2; // NullPointerException
- null 调用 instanceof 方法永远返回 false;
Object obj = null;
if (obj instanceof Object){System.out.println("True");
} else {System.out.println("False");
}
- null == null 返回 true 可以使用 == 和 != 来判断是否为 null;
Object obj = null;
if (obj == null){System.out.println("True");
} else {System.out.println("False");
}
null 是值而不是类型,所以不能当做 instanceof 的参数;
不能使用值为 null 的引用类型变量来调用非静态方法;
可以使用值为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 时调用静态方法正常执行,调用实例方法则会空指针异常;
- null 可以作为参数传递给方法,但是否崩溃取决于方法内部的处理;
六、总结
- OC 中的 Nil、nil、NULL 完全等价;
- C++ 不支持隐式类型转换而 C 支持,所以 C++ 中对 NULL 的定义做了特殊处理,直接定义为 0;
- C++ 支持方法重载,NULL 在 GCC 编译器中会对重载方法的调用产生误差,调用到 int 类型,在 C++ 编译器中直接报错;
- 因为 NULL 的定义不是很清晰所以出现了 nullptr 代替 NULL,表示空指针,nullptr 用于指针类型,赋值给非指针类型时直接编译期报错;
- NULL 虽然是 0,但是是 long 类型;
- Java 中的 null 是一种特殊的值,表示引用类型的指针为空;
Java、OC、C/C++中的null相关推荐
- java json 去除空_详解Java去除json数据中的null空值问题
1.描述 @JsonInclude(JsonInclude.Include.NON_NULL)标记是jackson包提供的json序列化方法,已经集成于Springboot2.0中,此方法的配置意在可 ...
- hutol json null值没了_详解Java去除json数据中的null空值问题
1.描述 @JsonInclude(JsonInclude.Include.NON_NULL)标记是jackson包提供的json序列化方法,已经集成于Springboot2.0中,此方法的配置意在可 ...
- 将JAVA bean/实体类 中为null的属性值转换成空字符串
使bean中为null的属性转换成空字符串 获得getter方法 方法有分带参数和不带参数,我们知道getter方法是不带参数的 获得getter方法如下 Method m = model.getCl ...
- java 静态类设置null_JAVA中对null进行强制类型转换(null可以强转为任意对象,并执行对象的静态方法)...
今天很好奇,对null进行强转会不会抛错.做了如下测试得到的结果是, 如果把null强转给对象,是不会抛异常的,本身对象是可以为null的. 但是如果是基本类型,比如 int i = (Integer ...
- Java中有关Null的9件事
对于Java程序员来说,null是令人头痛的东西.时常会受到空指针异常(NPE)的骚扰.连Java的发明者都承认这是他的一项巨大失误.Java为什么要保留null呢?null出现有一段时间了,并且我认 ...
- java8避免null_在 Java 8 中避免 Null 检查
如何预防 Java 中著名的 NullPointerException 异常?这是每个 Java 初学者迟早会问到的关键问题之一.而且中级和高级程序员也在时时刻刻规避这个错误.其是迄今为止 Java ...
- java 返回空数组_避免在Java中检查Null语句
1.概述 通常,在Java代码中处理null变量.引用和集合很棘手.它们不仅难以识别,而且处理起来也很复杂.事实上,在编译时无法识别处理null的任何错误,会导致运行时NullPointerExcep ...
- java中的null类型---有关null的9件事
摘自 https://blog.csdn.net/qq_25077777/article/details/80174763 今天听到一个问题,java中的null类型,null竟然是一种类型 java ...
- java 类 null_深入理解java中的null“类型”
本文研究的主要是java中的null"类型"的相关实例,具体介绍如下. 先给出一道简单的null相关的题目,引发我们对null的探讨,后面会根据官方语言手册对null"类 ...
- Java中的null是什么?
As we know null is an important concept in every language not only in Java but here we will study va ...
最新文章
- MySQL DBA基本知识点梳理和查询优化
- Dataset:(公交车、恐龙、大象、花朵、骏马)六类图片数据集(AutoKeras测试)的简介、下载、使用方法之详细攻略
- LeetCode 49字母异位词分组50pow(x,n)51八皇后
- php5.6的apaches的dll_Windows 10下 搭建Apache2.4、php5.6、mysql5.6
- LeetCode176 第二高的薪水
- UVA - 1314 Hidden Password(最小表示法)
- MATLAB字符转数据
- 数据库性能自动压测-Oracle swingbench篇
- .Net学习(二):vb.net总结之似曾相识
- POJ-2488 A Knights Journey-深度优先搜索DFS
- C语言查看tuxedo队列长度,tuxedo的常用启、停、参数讲解、日志查看
- 2.3全卷积网络(FCN)与图像分割
- 32.从1到n整数中1出现的次数
- LINUX下载编译libpng
- IEEE论文模板下载地址及说明
- 康托尔点集matlab实数,为什么康托尔集内的数和实数个数一样多
- Mybatis的一级缓存和二级缓存,看完不再懵逼
- 离职员工删库跑路,3个方法找回数据,有备无患
- PS 滤镜算法原理 ——马赛克
- css 文本、文字展开与收缩,查看更多收起