C++中NULL和0的区别是什么?c++11中nullptr与NULL的区别?

本文详细解析了C++中NULL、0、''和nullptr的区别与应用场景,包括它们在指针赋值、字符串操作及异常处理中的使用,并讨论了C++11中引入nullptr的原因。

C++中“NULL”和“0”的区别是什么?

  • 0是一个数字,本身有着一些原生的特性诸如:起始,没有,正负的分界线,0/1中的0还可以表示否定。0可以表示数字0,也可以表示ASCII码值为0的字符。

  • NULL用于将指针指定为空指针,也就是说该指针不指向任何对象,不可以对该指针进行解引用,否则系统会报错。

  • 在数值上NULL,'\0',0是一样的,都是0,但'0'就不同了,在ASCII码中编码为48,所以字符0和上述三个值不同。

  • 什么是 NULL?

    [6.3.2.3-Footnote] The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant

    即 NULL 是一个标准规定的宏定义,用来表示空指针常量。因此,除了上面的各种赋值方式之外,还可以用 p = NULL; 来使 p 成为一个空指针。

  • 指针的概念

    指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区。

  • malloc 函数在分配内存失败时返回 0 还是 NULL?

    malloc 函数是标准 C 规定的库函数。在标准中明确规定了在其内存分配失败时返回的是一个 “null pointer”(空指针):

    [7.20.3-1] If the space cannot be allocated, a null pointer is returned.

    对于空指针值,一般的文档(比如 man)中倾向于用 NULL 表示,而没有直接说成 0。但是我们应该清楚:对于指针类型来说,返回 NULL 和 返回 0 是完全等价的,因为 NULL 和 0 都表示 “null pointer”(空指针)。

  • ‘\0’:‘\0’是一个“空字符”常量,它表示一个字符串的结束,它的ASCII码值为0。注意它与空格' '(ASCII码值为32)及'0'(ASCII码值为48)不一样的。在C/C++中,当要给一个字符串添加结束标志时,都应该用‘\0’而不是NULL或0。

  • 当我们要置一个指针为空时,应该用NULL,当我们要给一个字符串添加结束标志时,应该用‘\0’。

 

c++11中nullptr与NULL的区别?

首先呢,要明白一点儿,NULL是一个无类型的东西,而且是一个宏。而宏这个东西,从C++诞生开始,就是C++之父嗤之以鼻的东西,他推崇尽量避免宏。而在他的FAQ中,也有相应的一个关于NULL与0的解释,也谈到了这一点儿。Stroustrup: C++ Style and Technique 

在C++标准中,我们可以见到一个词语叫做null pointer constant,其实在C++11标准前,是只承认0为null pointer constant的。所以,在C++中,我们也经常能听到一个说法,就是赋予null pointer,应该是使用0,而非NULL。而nullptr pointer constant这个词语在C++11发布后,终于再添了一个成员,就是nullptr。而与NULL本质不同的是,nullptr是有类型的(放了在stddef头文件中),类型是 typdef decltype(nullptr) nullptr_t; 而正是因为是有类型的,这给我们编译器实现nullptr的时候带来了更多细节的考虑,当然也给了使用者更多的保障,所以如果你的编译器支持nullptr,请一定使用nullptr!

而nullptr的出现背景,其实是很简单的,C++哲学上来说就是C++之父一直对null pointer没有一个正式的表示感到非常不满,而更工程的来说,就是关于重载这个问题。

void f(void*)
{
}

void f(int)
{
}

int main()
{
    f(0); // what function will be called?
}

而引入了nullptr,这个问题就得到了真正解决,会很顺利的调到void f(void*)这个版本。

好了,真的以为nullptr就这样了么? 我前面说过了nullptr是有类型的,叫做nullptr_t,这给我们编译器实现带来了诸多要考虑的东西,不幸的话让我们来举点儿奇葩例子吧!

union U
{
    long i;
    nullptr_t t;
};

int main()
{
    U u;
    u.i = 3;
    printf("%ld\n",(long)u.t); // What it is? 0 or 3?
}
那么这是应该符合union语意还是nullptr的语意呢?这在标准中是没有说的,我们也为此争论了非常久。当然在我们编译器的实现还是保持了nullptr的语意,结果是0。

而nullptr有类型后,还能做什么呢?那当然就是可以捕获异常了。

int main()
{
  try
  {
    throw nullptr;  
  }
  catch(nullptr_t)
  {

  } 
}
你扔一个NULL试试?看他应该用什么收,正是因为没有类型,所以就要用它的本质类型,比如long什么的来说。你扔一个0试试?那就也不是所谓的空指针类型了,就是要用int什么的来收了。

所以,推崇nullptr是有道理的,我们在编译器实现nullptr的时候考虑了非常非常多的细节,还有很多你们可能一直用不到的情况,我们都要用来测试,目的就是保障开发者的使用。再次那句话,如果你的编译器支持nullptr,请一定使用nullptr!

1、为什要有nullptr

      我们给一个指针赋初值的时候一般这么写  FILE* fp = NULL;      这里有个NULL的定义,一般情况下它是这么定义的:     [cpp]  #ifdef __cplusplus  #define NULL    0  #else  #define NULL    ((void *)0)  #endif         在c语言环境下,由于不存在函数重载等问题,直接将NULL定义为一个void*的指针就可以完美的解决一切问题。      但是在c++环境下情况就变得复杂起来, 首先我们不能写这样的代码   FILE* fp = (void*)0;   将void*直接赋值给一个指针是不合法的,编译器会报错。 我们只能这样写代码[cpp]  FILE* fp = (FILE*)0;  // or  FILE* fp = 0;     所以在c++下面,NULL就被直接定义为一个整型 0。  在大多数情况下这并不会产生什么问题,但是万一有重载或者模板推导的时候,编译器就无法给出正确结果了。比如下面的情形:[cpp]  void call_back_process(CCObject* target, void* data);  bind(call_back_process, target, NULL);   // error 函数类型是void* ,但是我们绑定的是一个整型 0  

2、 nullptr的应用场景:       如果我们的编译器是支持nullptr的话,那么我们应该直接使用nullptr来替代NULL的宏定义。正常使用过程中他们是完全等价的。

3、模拟nullptr的实现:      某些编译器不支持c++11的新关键字nullptr,我们也可以模拟实现一个nullptr[cpp]

 www.2cto.com

const  class nullptr_t_t  {  

public:     

template<class T>          

operator T*() const {return 0;}     

template<class C, class T>  operator T C::*() const { return 0; }  

private:     

void operator& () const;  }

nullptr_t = {};

 #undef NULL  

#define NULL nullptr_t  

<think>好的,我现在需要回答用户关于C++NULLnullptr区别的问题。用户之前已经询问过struct astruct *a的区别,现在转向更基础的指针指针常量的问题。我需要确保回答准确且易于理解。 首先,我要回顾一下自己关于NULLnullptr的知识。NULLC++中通常是一个宏定义,传统上被定义为0或者(void*)0。而nullptrC++11引入的关键字,专门表示空指针。两者的主要区别在于类型安全重载解析时的行为不同。 接下来,我需要整理出它们的区别点。可能包括以下几点: 1. **定义来源**:NULL是宏,nullptr是关键字。 2. **类型安全**:NULL可能被隐式转换为整数类型,导致类型不安全的操作;nullptr具有明确的指针类型,不会被误认为整数。 3. **重载函数匹配**:当函数有重载接受指针整数时,使用NULL可能导致调用错误的重载版本,而nullptr会正确选择指针版本。 4. **代码清晰性**:使用nullptr能更明确地表示指针的意图。 5. **C++标准的支持**:nullptrC++11及之后的标准,NULL则更早。 然后,我需要考虑如何用例子来说明这些区别。比如,重载函数的例子可以展示NULLnullptr的不同行为。类型转换的问题也需要通过代码示例来体现。 同时,要注意用户可能的背景。他们可能对C++有一定了解,但可能不熟悉C++11的新特性。因此,解释时要简明,避免过于技术化的术语,但也要准确。 需要确认是否正确处理了C中的NULLC++中的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、付费专栏及课程。

余额充值