NULL和0的区别,nullptr的来历

本文深入探讨了C/C++中空指针的概念,解释了NULL与0的区别,以及为何C++11引入了nullptr。通过代码示例,展示了不同情况下指针初始化的选择,及其在函数重载中的应用。

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

转载自:https://www.cnblogs.com/malecrab/p/5569707.html

 

某些时候,我们需要将指针赋值为空指针,以防止野指针。

 

有人喜欢使用NULL作为空指针常量使用,例如:int* p = NULL;。

也有人直接使用0值作为空指针常量,例如:int* p = 0;。

 

前者可能觉得:NULL作为空指针常量,名字很形象,可读性较强。

后者可能觉得:NULL并不是C/C++语言的关键字,而是一个在标准库头文件<stddef.h>中定义的宏,因此要使用NULL,可能需要直接或简介地包含<stddef.h>头文件,比较麻烦。

 

问题一:NULL与常数0值有何区别?

 

要弄清楚这个问题,我们采用问与答的形式来描述。

 

问:NULL到底是什么?

 

答:NULL是一个宏。

 

问:它的值是多少?

 

答:C/C++标准规定:它的值是一个空指针常量(null pointer constant),由实现定义。#1,#2

 

问:什么样的值才能称之为空指针常量?

 

答:C语言中常数0和(void*)0都是空指针常量;C++中(暂且忽略C++11)常数0是,而(void*)0 不是。#3,#4

 

问:NULL宏是在哪里定义的?

 

答:通常是在C标准库的<stddef.h>头文件中,不过别的头文件中可能也有定义。

 

问:一般编译器的<stddef.h>头文件中NULL宏是如何定义的?

 

答:以gcc或clang编译器为例,NULL的定义大致如下(稍有简化):

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

 

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

 

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

 

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

 

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

 

问题二:C++11中为什么要引入nullptr?

 

考虑着这样一个函数重载的情形:

 

#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,它作为一种空指针常量。#7例如:

 

void foo(int) {}     // #1
void foo(char*) {}   // #2
int main() {
    foo(nullptr); // 它会毫无异议地调用#2
}

 

附注:

 

[#1] C99: 7.17-p3:

    The macros are

        NULL

    which expands to an implementation-defined null pointer constant; and ...

 

[#2] C++03: 18.1-p4:

    The macro NULL is an implementation-defined C + + null pointer constant in this International Standard(4.10).

 

[#3] C99: 6.3.2.3-p3:

    An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.

 

[#4] C++03: 4.10-p1:

    A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero.

 

[#5] C99: 6.3.2.3-p1:

    A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

 

[#6] C++03: 4.10-p2:

    An rvalue of type “pointer to cv T,” where T is an object type, can be converted to an rvalue of type “pointer to cv void.”

 

[#7] C++11: 4.10-p1:

    A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t.

 

参考:

 

 

 

 

 

 

 

<think>嗯,用户问的是C++中0NULLnullptr区别及使用场景。我需要先回忆一下这三者的背景各自的特点。根据之前的引用资料,用户已经提到过一些关键点,比如NULL在C++中可能被定义为0,而nullptr是C++11引入的新关键字,类型安全的空指针常量。首先,我需要明确这三个概念的历史背景。在C语言中,NULL通常被定义为(void*)0,但在C++中,由于类型系统的严格,NULL被定义为整数0。这可能引发一些问题,比如在函数重载时,传递NULL可能被当作整型0而不是指针,导致调用错误的函数。而nullptr的引入正是为了解决这种类型安全问题。接下来,用户可能想知道它们的具体区别。比如,0是一个整数字面量,而NULL在C++中是0的宏定义,nullptr则是std::nullptr_t类型的实例。需要举例说明它们在不同场景下的行为差异,比如在函数重载时,使用NULL可能导致歧义,而nullptr能明确调用指针版本。然后,使用场景方面,用户需要知道什么时候应该使用哪个。比如在C++11及以上版本中,推荐使用nullptr代替NULL,以避免类型相关的问题。而0主要用于整数上下文,而不是指针。还需要注意类型安全的问题。nullptr不能被隐式转换为整数,而NULL0可以,这可能导致意外的错误。比如,当传递NULL到一个期望指针的函数时,如果存在重载的整型版本,可能会调用错误。 另外,用户可能关心它们的实际值。根据引用中的例子,nullptr的值在输出时显示为0,但它的类型是特殊的,不会被当作整数处理。而NULL作为宏,实际值也是0,但类型可能不同。最后,总结三者的区别,给出使用建议,并生成相关问题。确保回答结构清晰,分点明确,用代码示例说明问题,并正确引用提供的资料。</think>### C++中0NULLnullptr区别及使用场景 --- #### **一、基本概念与历史背景** 1. **`0`的本质** - 原始的整数字面量,在指针上下文中可隐式转换为空指针 - 示例: ```cpp int* ptr = 0; // 合法但存在风险 ``` 2. **`NULL`的定位** - C语言中定义为`((void*)0)`,但在C++中通常定义为整数`0` - 示例(C++标准库定义): ```cpp #define NULL 0 // 常见实现方式 ``` 3. **`nullptr`的引入** - C++11新增的关键字,类型为`std::nullptr_t` - 专用于表示空指针,无整型歧义 ```cpp int* ptr = nullptr; // 类型安全的空指针 ``` --- #### **二、核心差异对比** | 特性 | 0 | NULL | nullptr | |-----------------|--------------------|-------------------|-------------------| | **类型** | `int` | 整型(通常为`int`) | `std::nullptr_t` | | **隐式转换** | 可转整型或指针 | 同0 | 仅转指针类型 | | **重载歧义** | 可能引发歧义 | 同0 | 明确调用指针版本 | | **类型安全** | ❌ | ❌ | ✔️ | --- #### **三、典型问题场景** 1. **函数重载歧义** ```cpp void func(int); // 重载1:接受整型 void func(int*); // 重载2:接受指针 func(0); // 调用重载1(整型)[^3] func(NULL); // 可能调用重载1(C++中NULL=0)[^2] func(nullptr); // 明确调用重载2(指针)[^3] ``` 2. **模板类型推导** ```cpp template<typename T> void check(T* ptr) { if (ptr == nullptr) { /*...*/ } } check(0); // 编译错误:无法推导T类型 check(nullptr); // 正确推导指针类型 ``` 3. **空指针验证** ```cpp int* p1 = 0; int* p2 = nullptr; if (p1 == p2) { /* 始终为true,但逻辑意图不明确 */ } ``` --- #### **四、使用场景建议** 1. **推荐使用`nullptr`的情况** - C++11及以上项目 - 需要明确指针语义的代码(如智能指针、模板) - 避免函数重载歧义时 2. **允许使用`0`的情况** - 历史遗留代码维护 - 明确需要整数`0`的上下文(如数组下标) 3. **避免使用`NULL`的情况** - 新C++项目中完全用`nullptr`替代 - 需要跨平台兼容性时(不同编译器对NULL的定义可能不同) --- #### **五、内存与值表现** - **指针值比较** ```cpp int* p = nullptr; cout << (p == 0); // 输出1(值相同,但语义不同) cout << (p == NULL); // 输出1(同上) ``` - **内存地址** ```cpp int* p = nullptr; cout << p; // 输出0x0(十六进制)或0(十进制)[^4] ``` --- #### **六、数学表达示例** 在指针运算中,空指针的偏移量计算为: $$ \text{ptr} + n = \text{ptr} + n \times \text{sizeof(T)} $$ 但若`ptr`为`nullptr`,此操作为**未定义行为**[^4] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值