C++11中的指针空值nullptr

本文探讨了C++11中引入的nullptr关键字及其类型nullptr_t的特点,解决了NULL宏定义带来的二义性问题,并详细说明了nullptr的使用场景及限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

时间:2014.05.07

地点:基地

-----------------------------------------------------------------------------

一、为什么要有指针空值nullptr

  在良好的编码习惯中,声明一个指针并对其进行初始化非常重要,未初始化的悬挂指针常导致一些难以调试的错误。往常,我们常这样初始化指针变量:

int* my_ptr1=0;
int* my_ptr2=NULL;
但我们知道,NULL其实是一个宏定义,字面常量为0,即上述两种方式本质上是一样的。于是,这样就有了一些麻烦,比如函数重载时,我们定义了如下两个版本的函数:

#include<iostream>
using namespace std;
void f(char* c)
{
	cout << "invoke f(char* c)" << endl;
}
void f(int i)
{
	cout << "invoke f(int)" << endl;
}
int main()
{
	f(0);
	f(NULL);
	f((char*)0);
	f(nullptr);
	return EXIT_SUCCESS;
}

结果如下:

可见,f(NULL)并没有调用想要的指针版本,而是调用了f(int)版本,这是因为NULL被定义为0,编译器总是优先把0视为一个整型常量造成的。0在C++98中是有二义性的,编译器首先解释它是一个整型常量,其次是一个无类型指针(void*)。

-----------------------------------------------------------------------------

二、问题的解决

  C++11中,首先为了兼容性考虑并没消除0的二义性,但给出了一种新的方案,使用指针空值nullptr去初始化指针。指针空值的类型被命名为nullptr_t。它是这么来的:

typedef decltype(nullptr) nullptr_t;
充分利用了decltype 。即nullptr也是有类型的。且仅可以被隐式转化为指针类型。于是f(nullptr)会调用f(char*)版本。这样用户也就能表达自己的意图了。

-----------------------------------------------------------------------------

三、类型nullptr_t

 上面我们已经知道nullptr的类型为nullptr_t,我们再看看这种类型有什么特点

1.nullptr_t是一种内置数据内型

2.nullptr_t类型 数据可以隐式转换为任意一种指针类型,但无论你想什么方式都不能转换为非指针类型。

3.nullptr_t类型数据不可用于算术运算

4.nullptr_t类型数据可以用于同类型数据之间进行关系运算。

所以诸如:

if(nullptr);
if(nullptr==0);
是不能编译通过的。

值得注意的是nullptr_t看起来像个指针类型,但其实不是的,它就是nullptr_t这样一种类型,比如在模板中就不能当做是一种指针类型传递给模板参数了,模板是推导不出它的具体类型的,必须显式转换:

template<typename T>void g(T* T){}
......
g(nullptr);   //编译错误
g((float*)nullptr);  //可推导出T为float

另外

sizeof(nullptr_t)==sizeof(void*)
即nullptr)_t所占的内存空间大小和void*相同,但他们两者的内涵是不一样的。nullptr是一个编译时期的常量,能为编译器识别。而(void*)0只是一个强制转换表达式,返回一个void* 指针类型的数据而已。而且nullptr到任何指针的转换都是隐式的。而且我们不要对nullptr进行去地址操作,因为它是一个右值常量,取nullptr的地址通常也没有意义。





<think>好的,我现在需要回答用户关于C++中NULL和nullptr的区别的问题。用户之前已经询问过struct a和struct *a的区别,现在转向更基础的指针和空指针常量的问题。我需要确保回答准确且易于理解。 首先,我要回顾一下自己关于NULL和nullptr的知识。NULL在C++中通常是一个宏定义,传统上被定义为0或者(void*)0。而nullptrC++11引入的关键字,专门表示空指针。两者的主要区别在于类型安全和重载解析时的行为不同。 接下来,我需要整理出它们的区别点。可能包括以下几点: 1. **定义来源**:NULL是宏,nullptr是关键字。 2. **类型安全**:NULL可能被隐式转换为整数类型,导致类型不安全的操作;nullptr具有明确的指针类型,不会被误认为整数。 3. **重载函数匹配**:当函数有重载接受指针和整数时,使用NULL可能导致调用错误的重载版本,而nullptr会正确选择指针版本。 4. **代码清晰性**:使用nullptr能更明确地表示指针的意图。 5. **C++标准的支持**:nullptrC++11及之后的标准,NULL则更早。 然后,我需要考虑如何用例子来说明这些区别。比如,重载函数的例子可以展示NULL和nullptr的不同行为。类型转换的问题也需要通过代码示例来体现。 同时,要注意用户可能的背景。他们可能对C++有一定了解,但可能不熟悉C++11的新特性。因此,解释时要简明,避免过于技术化的术语,但也要准确。 需要确认是否正确处理了C中的NULL和C++中的NULL的区别。在C++中,NULL通常是0,而在C中可能是(void*)0,但C++不允许将void*隐式转换为其他指针类型,因此C++中的NULL定义为0更为常见。 另外,要提到在C++11之后推荐使用nullptr代替NULL,以提高代码的安全性和可读性。 可能还需要提到nullptr的类型是std::nullptr_t,可以隐式转换为任何指针类型,但不能转换为整数类型,这有助于避免错误。 最后,要确保回答结构清晰,分点列出,并附上代码示例,帮助用户理解。</think>在C++中,`NULL`和`nullptr`都用于表示空指针,但它们的实现机制和使用场景有显著区别。以下是详细对比: --- ### 一、本质区别 | 特性 | `NULL` | `nullptr` (C++11引入) | |---------------------|---------------------------------|-----------------------------| | **本质** | 宏定义(通常为`0`或`(void*)0`) | 关键字(类型安全的空指针常量)| | **类型** | 可能被识别为`int`或指针 | 明确的`std::nullptr_t`类型 | | **隐式转换能力** | 可隐式转为`int`或指针 | 只能转为指针类型 | | **代码安全性** | 容易引发类型错误 | 类型安全 | --- ### 二、典型问题场景 #### 1. 重载函数歧义问题 ```cpp void func(int x) { // 重载1:处理整数 cout << "int: " << x << endl; } void func(char* ptr) { // 重载2:处理指针 cout << "pointer: " << (ptr ? "valid" : "null") << endl; } int main() { func(NULL); // 可能调用func(int)(编译器警告) func(nullptr); // 明确调用func(char*) return 0; } ``` - **输出结果**: ``` int: 0 // 使用NULL的意外行为 pointer: null // nullptr正确触发指针版本 ``` #### 2. 模板类型推导问题 ```cpp template<typename T> void check_type(T value) { if constexpr (is_same_v<T, int>) { cout << "Detected int" << endl; } else if constexpr (is_same_v<T, char*>) { cout << "Detected pointer" << endl; } } int main() { check_type(NULL); // 输出"Detected int"(不符合预期) check_type(nullptr); // 输出"Detected pointer" return 0; } ``` --- ### 三、底层实现对比 #### `NULL`的典型定义(C++标准库实现): ```cpp #define NULL 0 // 或 #define NULL (void*)0 // 在C中常见,但C++不允许void*隐式转换 ``` #### `nullptr`的特性: - 类型为`std::nullptr_t`(定义在`<cstddef>`中) - 可隐式转换为任何指针类型(包括类成员指针) - **不能**转换为整数类型: ```cpp int x = nullptr; // 编译错误 ``` --- ### 四、使用场景建议 | 场景 | 推荐选择 | 原因 | |---------------------|-------------|---------------------------------------| | 所有指针空值初始化 | `nullptr` | 类型安全,避免歧义 | | 旧代码维护 | `NULL` | 兼容C++11之前的代码 | | 模板编程 | `nullptr` | 确保类型推导正确 | | 与C语言交互 | `NULL` | 符合C语言习惯(但需注意类型转换风险) | --- ### 五、关键注意事项 1. **不要混用两者**: ```cpp int* p1 = NULL; // 传统写法(不推荐) int* p2 = nullptr; // 现代C++推荐写法 ``` 2. **与`delete`配合使用时等价**: ```cpp delete NULL; // 安全(无操作) delete nullptr; // 同样安全 ``` 3. **C++11后的统一标准**: ```cpp auto ptr = nullptr; // 自动推导为std::nullptr_t ``` --- ### 六、完整代码示例 ```cpp #include <iostream> #include <type_traits> using namespace std; void demo(int x) { cout << "Received int: " << x << endl; } void demo(char* ptr) { cout << "Received pointer: " << (ptr ? "valid" : "null") << endl; } int main() { demo(NULL); // 可能触发警告,输出"Received int: 0" demo(nullptr); // 明确调用指针版本 // 类型检测示例 cout << "Type of NULL: " << typeid(NULL).name() << endl; // 输出'i'(int) cout << "Type of nullptr: " << typeid(nullptr).name() << endl; // 输出'Dn'(std::nullptr_t) return 0; } ``` --- **总结**:`nullptr`是C++11引入的类型安全空指针常量,彻底解决了`NULL`在重载、模板和类型推导中的二义性问题。在新代码中应始终优先使用`nullptr`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值