1. 空指针

在谈nullptr之前,我们先聊聊C++中的空指针:
在指针变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。NULL 指针是一个定义在标准库中的值为零的常量。例如:

#include <iostream>
//程序将输出:ptr 的值是 0
using namespace std;

int main ()
{
   int  *ptr = NULL;
   // int *ptr = 0; 

   cout << "ptr 的值是 " << ptr ;
 
   return 0;
}

在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。如需检查一个空指针,可以使用 if 语句,如下所示:

if(ptr)     /* 如果 ptr 非空,则完成 */
if(!ptr)    /* 如果 ptr 为空,则完成 */
//个人常用的方式:
if(ptr!=NULL)       /* 如果 ptr 非空,则完成 */
if(ptr==NULL)   /* 如果 ptr 为空,则完成 */

所以,综上所述,声明一个空指针可有两种方式:intp=NULLintp=0int* p = NULL 或 int* p = 0
此处对NULL常数0进行一些整理:

  • NULL到底是什么?NULL的实质是一个在标准库头文件<stddef.h>中定义的宏。
  • 它的值是多少?C/C++标准规定:它的值是一个空指针常量(null pointer constant),由实现定义。
  • 什么样的值才能称之为空指针常量?C语言中常数0和(void)0(void*)0都是空指针常量;C++中(暂且忽略C++11)常数0是,而(void)0(void*)0不是。

其实我们查看对NULL的定义即可明白:

if defined(__cplusplus)
define NULL 0    // C++中使用0作为NULL的值
else
define NULL ((void *)0)    // C中使用((void *)0)作为NULL的值
endif

2. nullptr

通过上述介绍,我们不难看出,在编程的世界里0有双重的角色,可以表示整数零,也可以表示一个空指针。在C语言中,通过预编译宏NULL,可以区分0表示的是零还是(void*)0。但是,在C++的世界中,这样是不可以的。加之C++中允许函数重载,这便对空指针的声明产生了影响,例如:

void foo(char *);
void foo(int);

如果把NULL定义为0,那么foo(NULL)将调用哪个函数呢?

#include <stddef.h>
void foo(int) {}     // #1
void foo(char*) {}   // #2
int main() {
    foo(NULL); // 调用#1还是#2?
}

从字面上来讲,NULL是个空指针常量,我们可能会觉得:既然是个指针,那么应该调用#2。但事实上调用的却是#1,因为C++中NULL扩展为常数0,它是int型。
为了解决常数0既是整数常量,也是空指针常量这样的二义性问题,C++11引入了另一个关键字nullptr,作为一个空指针。例如:

char *pc = nullptr;     // OK
int  *pi = nullptr;     // OK
bool   b = nullptr;     // OK. b is false.
int    i = nullptr;     // error

foo(nullptr);           // calls foo(char *), not foo(int);