'\ 0'和NULL可以互换使用吗?

时间:2016-11-03 06:23:49

标签: c++ null

NULL通常用在指针的上下文中,并通过多个标准库(例如<iostream>)中的宏定义为整数0'\0'是空字符,是8位零。顺便提一下,8位零等价于整数0

在某些情况下,虽然它被认为是可怕的风格,但这两者可以互换:

int *p='\0';
if (p==NULL) //evaluates to true
    cout << "equal\n";

或者

char a=NULL;
char b='\0';
if (a==b) //evaluates to true
    cout << "equal again\n";

关于SO已经有很多类似的问题;例如,这个问题的最佳答案(What is the difference between NULL, '\0' and 0)说“它们实际上不是一回事”。

是否有人可以提供NULL\0无法互换的示例(最好是实际申请而不是病态案例)?

7 个答案:

答案 0 :(得分:51)

  

有没有人能提供NULL和\ 0不能互换的例子?

NULL'\0'之间的差异可能会影响重载解析。

示例(check it on Coliru):

#include <iostream>

// The overloaded function under question can be a constructor or 
// an overloaded operator, which would make this example less silly
void foo(char)   { std::cout << "foo(char)"  << std::endl; }
void foo(int)    { std::cout << "foo(int)"   << std::endl; }
void foo(long)   { std::cout << "foo(long)"  << std::endl; }
void foo(void*)  { std::cout << "foo(void*)" << std::endl; }

int main()
{
    foo('\0'); // this will definitely call foo(char)
    foo(NULL); // this, most probably, will not call foo(char)
}

请注意,Coliru使用的gcc编译器将NULL定义为0L,对于此示例,这意味着foo(NULL)解析为foo(long)而不是foo(void*)This answer详细讨论了这一方面。

答案 1 :(得分:36)

C ++中宏NULL的定义

Leon is correct当同一个函数有多个重载时,\0更喜欢带有char类型参数的重载。但是,重要的是要注意在典型的编译器上,NULL更喜欢带有int类型参数的重载,而不是void*类型的参数!

可能导致这种混淆的原因是C语言允许将NULL定义为(void*)0。 C ++标准明确声明 (草案N3936,第444页)

  

宏[{1}}]的可能定义包括NULL0,但不包括0L

这种限制是必要的,因为例如(void*)0是有效的C但是C ++无效,而char *p = (void*)0在两者中都有效。

在C ++ 11及更高版本中,如果需要一个行为指针的空常量,则应使用char *p = 0

莱昂的建议如何在实践中发挥作用

此代码定义了单个函数的多个重载。每个重载都输出参数的类型:

nullptr

On Ideone此输出

#include <iostream>

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

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

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

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

int main() {
    f(0);
    f(NULL);
    f('\0');
    f(nullptr);
}

因此,我会声称重载问题不是实际应用,而是病态案例。 int int char void* 常量无论如何都会出错,应该在C ++ 11中用NULL替换。

如果NULL不为零怎么办?

另一个问题的另一个病态案例is suggested by Andrew Keeton

  

请注意,C语言中的空指针是什么。它与底层架构无关。如果底层架构的空指针值定义为地址0xDEADBEEF,那么由编译器来解决这个问题。

     

因此,即使在这个有趣的架构上,以下方法仍然是检查空指针的有效方法:

nullptr
     

以下是检查空指针的INVALID方法:

if (!pointer)
if (pointer == NULL)
if (pointer == 0)
     

因为这些被编译器视为正常比较。

摘要

总而言之,我会说差异主要是风格。如果你的函数需要#define MYNULL (void *) 0xDEADBEEF if (pointer == MYNULL) if (pointer == 0xDEADBEEF) 并且重载需要int,并且它们的功能不同,那么当你使用char\0常量调用它们时会发现不同。但是只要将这些常量放在变量中,差异就会消失,因为调用的函数是从变量的类型中扣除的。

使用正确的常量可以使代码更易于维护,并更好地传达意义。当你的意思是一个数字时,你应该使用NULL;当你指的是一个字符时,你应该使用0;当你的意思是一个指针时,你应该使用\0Matthieu M.在评论中指出GCC had a bug,其中nullptrchar*进行了比较,而目的是取消引用指针并比较\0char。如果在代码库中使用了合适的样式,则更容易检测到这些错误。

要回答您的问题,实际上并没有实际的用例会阻止您互换使用\0\0。只是风格的原因和一些边缘情况。

答案 2 :(得分:8)

请不要这样做。这是一种反模式,实际上是错误的。 NULL用于NULL指针,'\0'是空字符。它们在逻辑上是不同的东西。

我认为我从未见过这个:

int* pVal='\0';

但这很常见:

char a=NULL;

但这不是好形式。它使代码不那么便携,在我看来,可读性较差。它也可能在混合C / C ++环境中引起问题。

它依赖于有关任何特定实现如何定义NULL的假设。例如,某些实现使用简单的

#define NULL 0

其他人可能会使用:

#define NULL ((void*) 0)

我已经看到其他人定义为整数,以及各种奇怪的处理。

在我看来,

NULL应仅用于表示无效地址。如果您想要一个空字符,请使用'\0'。或者将其定义为NULLCHR。但那不是那么干净。

这将使您的代码更具可移植性 - 如果您更改编译器/环境/编译器设置,则不会开始收到有关类型等的警告。这在C或混合C / C ++环境中可能更为重要。

可能出现的警告示例:请考虑以下代码:

#define NULL 0
char str[8];
str[0]=NULL;

这相当于:

#define NULL 0
char str[8];
str[0]=0;

我们正在为char分配一个整数值。这可能会导致编译器警告,如果有足够多的情况,很快您就看不到任何重要的警告。对我来说,这是真正的问题。代码中有警告有两个副作用:

  1. 如果有足够的警告,你就不会发现新的警告。
  2. 它表示警告是可以接受的。
  3. 在这两种情况下,实际的错误都可以通过,如果我们打扰阅读警告(或打开-Werror),编译器会捕获这些错误

答案 3 :(得分:8)

是的,在解决重载功能时,它们可能表现出不同的行为。

func('\0')调用func(char)

func(NULL)调用func(integer_type)

您可以使用nullptr来消除混淆,{{3}}始终是指针类型,在分配/比较值或函数重载分辨率时不显示歧义。

char a = nullptr; //error : cannot convert 'std::nullptr_t' to 'char' in initialization
int x = nullptr;  //error : nullptr is a pointer not an integer

请注意,它仍与NULL兼容:

int *p=nullptr;
if (p==NULL) //evaluates to true

摘自C ++ Programming Stroustrup第4版书籍:

  

在旧代码中,通常使用0或NULL而不是nullptr   (§7.2.2)。但是,使用nullptr可以消除潜在的混淆   整数之间(如0或NULL)和指针(如nullptr)。

答案 4 :(得分:7)

计算机程序有两种类型的读者。

第一种类型是计算机程序,如编译器。

第二种类型是人类,就像你和你的同事一样。

程序通常很好,一种类型的零代替另一种类型。 正如其他答案所指出的那样,也有例外,但这并不重要。

重要的是你正在弄乱人类读者。

人类读者非常上下文敏感。通过使用错误的零,你对人类读者撒谎。他们会诅咒你。

被骗的人可以更容易地忽视虫子 被骗的人可以看到不存在的“虫子”。当“修复”这些phanthom错误时,它们会引入真正的错误。

不要欺骗你的人类。你所撒谎的人之一就是你未来的自我。你也会诅咒你。

答案 5 :(得分:4)

摘自C ++ 14草案N3936:

  

18.2类型[support.types]

     

3宏NULL是本国际标准(4.10)中一个实现定义的C ++空指针常量。

     

4.10指针转换[conv.ptr]

     

1 空指针常量是一个整数文字(2.14.2),其值为零或prvalue类型为std::nullptr_t
  空指针常量可以转换为指针类型;结果是该类型的空指针值,并且可以与对象指针或函数指针类型的每个其他值区分开来。

因此,NULL可以是值为零的任何整数文字,或类型为std::nullptr_t的{​​{1}}类型的值,而nullptr始终为零窄字符文字

所以,通常不可互换,即使在指针上下文中你也看不到任何风格差异。

一个例子是:

'\0'

答案 6 :(得分:1)

让我们定义C / C ++中的NULL。

根据C / C ++引用,NULL被定义为扩展为空指针常量的宏。接下来我们可以读到空指针常量可以转换为任何指针类型(或指向成员的指针类型),它获取空指针值。这是一个特殊值,表示指针未指向任何对象。

参考C:

的定义
  

空指针常量是一个整数常量表达式   计算结果为零(如0或0L),或者将此类值转换为类型   void *(like(void *)0)。

参考C ++ 98的定义:

  

空指针常量是一个整数常量表达式,其计算结果为零(例如0或0L)。

参考C ++ 11的定义:

  

空指针常量是一个求值为零的整型常量表达式(如0或0L),或类型为nullptr_t的值(如nullptr)。

重载方法示例。

我们假设我们有以下方法:

class Test {
public:
    method1(char arg0);
    method1(int arg0);
    method1(void* arg0);
    method1(bool arg0);
}

使用参数NULLnullptr调用method1应调用method1(void* arg0);。但是,如果我们使用参数'\0'0调用method1,则应执行method1(char arg0);method1(int arg0);