【C++】NULL和nullptr的关联与差别

在写代码的过程中,有时候需要将指针赋值为空指针,以防止野指针。在C中,都是使用NULL来实现的;在C++中,除了NULL之外,还提供了nullptr来进行定义。那么两者之间有什么区别呢,分别适用于什么类型的场景呢?


NULL在C/C++中的含义

NULL是一个宏定义,它的值是一个空指针常量,由实现来进行定义。C语言中常数0和(void*)0都是空指针常量;C++中常数0是,而(void*)0 不是。

那问题来了,为什么C中(void*)0是空指针常量,而C++中不是?

因为C语言中任何类型的指针都可以(隐式地)转换为void* 型,反过来也行;而C++中void* 型不能隐式地转换为别的类型指针(例如:int* p = (void*)0,使用C++编译器编译会报错)。

可以查看到NULL的宏定义内容:NULL在C和C++中的定义不同,C中NULL为(void * )0,而C++中NULL为整数0

// C语言中NULL定义
#define NULL (void*)0                //c语言中NULL为void类型的指针,但允许将NULL定义为0

// C++中NULL的定义
#ifndef NULL
#ifdef _cpluscplus                       //用于判定是c++类型还是c类型
#define NULL 0                         //c++中将NULL定义为整数0
#else
#define NULL ((void*)0)             //C语言中NULL为void类型的指针
#endif
#endif

那既然C/C++标准中,常数0都可作为空指针常量,为什么不统一使用0?

可能觉得由于(void * )0更能体现指针的意义,而常数0更多的时候是用作整数。因此,C语言中NULL定义选择了(void*)0。

因此,如果在C++中如下定义:

int *p = NULL;

实际表示将指针p的值赋为0,而C++中当一个指针的值为0时,认为指针为空指针。

但是,如果单纯这样设计,在使用过程中可能会产生一个问题:

#include <iostream>

void f(int) {
  std::cout <<"invoke f(int)" << std::endl;
}

void f(void*) {
  std::cout << "invoke f(void*)" << std::endl;
}

int main(int argc, char *argv[]) {
	f(0);
  f(NULL);

	return 0;
}

当实参是NULL的时候,到底表示的是0调用f(int)函数,还是表示指针调用f(void*)呢?绝大多数编译器都是选择调用f(int)函数,当然也有的编译器直接编译出错。


nullptr

为了避免上面的情况出现,C++11引入了一个新关键字nullptr(也有的称之为:空指针常量),它的类型为std::nullptr_t。在C++中,void * 不能隐式地转化为任意类型的指针(可以强制转化),但空指针常数可以隐式地转换为任意类型的指针类型

nullptr与nullptr_t

stddef.h中有如下的描述:

typedef decltype(nullptr) nullptr_t;
  1. nullptr_t是一种数据类型,而nullptr是该类型的一个实例。通常情况下,也可以通过nullptr_t类型创建另一个新的实例;
  2. 所有定义为nullptr_t类型的数据都是等价的,行为也是完全一致的;
  3. std::nullptr_t类型,并不是指针类型,但可以隐式转换成任意一个指针类型(注意不能转换为非指针类型,强转也不行)
  4. nullptr_t类型的数据不适用于算术运算表达式。但可以用于关系运算表达式(仅能与nullptr_t类型数据或指针类型数据进行比较,当且仅当关系运算符为==、<=、>=等时)。

nullptr与NULL的区别

  1. NULL是一个宏定义,C++中通常将其定义为0,编译器一般优先把它当作一个整型常量(C标准下定义为(void*)0);
  2. nullptr是一个编译期常量,其类型为nullptr_t。它既不是整型类型,也不是指针类型;
  3. 在模板推导中,nullptr被推导为nullptr_t类型,仍可隐式转为指针。但0或NULL则会被推导为整型类型
  4. 要避免在整型和指针间进行函数重载。因为NULL会被匹配到整型形参版本的函数,而不是预期的指针版本

nullptr与(void*)0的区别

  1. nullptr到任意类型指针的转换是隐式的(尽管nullptr不是指针类型,但仍可当指针使用);
  2. (void*)0只是一个强制转换表达式,其返回void*指针类型,只能经过类型转换到其他指针才能用。

例如:

#include <iostream>

int main(int argc, char *argv[]) {
  void* px = NULL;
  // int* py = (void*)0;         //编译错误,不能隐式将void*转为int*类型
  int* pz = (int*)px;           //void*不能隐式转为int*,必须强制转换!

  int* pi = nullptr;            //ok!nullptr可以隐式转为任何其他指针类型
  void* pv = nullptr;           //ok! nullptr可以隐式转为任何其他指针类型

	return 0;
}

总结

NULL在C语言中是(void * )0,在C++中却是0。这是因为在C++中void * 类型是不允许隐式转换成其他指针类型的,所以之前C++中用0来代表空指针。但是,在重载整型和指针的情况下,会出现匹配错误的情况。所以,C++11加入了nullptr,可以保证在任何情况下都代表空指针


相关阅读

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值