'0'和NULL可以交替使用吗?

NULL通常用于指针的上下文中,并通过多个标准库(如<iostream> )中的macros定义为整数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已经有很多类似的问题。 例如,这个问题的最佳答案( NULL,'\ 0'和0之间的区别是什么 )说“他们不是真的是一回事”。

任何人都可以提供一个例子, NULL\0不能互换(最好是一个实际的应用程序,而不是一个病态)?

任何人都可以提供一个例子,NULL和\ 0不能互换?

NULL'\0'之间的区别可能会影响重载parsing。

例子( 在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)将parsing为foo(long)而不是foo(void*) 。 Ville-Valtteri Tiittanen的回答详细讨论了这方面。

在C ++中定义macrosNULL

Leon是正确的 ,当同一个函数有多个重载的时候, \0会喜欢那个接受chartypes的参数。 但是,重要的是要注意,在典型的编译器中, NULL更喜欢使用inttypes的参数,而不是void*types的重载。

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

macrosNULL可能定义包括00L ,但不包括(void*)0

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

在C ++ 11和更高版本中,如果需要一个作为指针行为的空常量,则应该使用nullptr

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

这段代码定义了单个函数的几个重载。 每个超载输出参数的types:

 #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); } 

在Ideone这个输出

 int int char void* 

因此,我认为超载的问题不是一个实际的应用,而是一个病态的案例。 无论如何, NULL常量将performance错误,应该用C ++ 11中的nullptrreplace。

如果NULL不是零呢?

Andrew Keeton在另一个问题上提出了另一个病理学案例:

注意什么是C语言中的空指针。 它在底层架构上并不重要。 如果底层架构有一个定义为地址0xDEADBEEF的空指针值,那么由编译器来sorting这个混乱。

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

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

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

 #define MYNULL (void *) 0xDEADBEEF if (pointer == MYNULL) if (pointer == 0xDEADBEEF) 

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

概要

总而言之,我会说差异主要是文体上的。 如果你有一个函数使用int和使用char重载,并且它们的function不同,当你用\0NULL常量调用它们时,你会注意到它们之间的区别。 但是,只要将这些常量放入variables中,差异就会消失,因为调用的函数将从variables的types中减去。

使用正确的常量可以使代码更易于维护,更好地传达意义。 你应该使用0表示数字, \0表示一个字符,而nullptr表示一个指针。 Matthieu M.在评论中指出, GCC有一个错误 ,其中char*\0进行比较,而目的是取消引用指针并将一个char\0进行比较。 如果在代码库中使用正确的样式,这样的错误更容易被检测到。

要回答你的问题,并没有一个实际的用例可以防止你使用\0NULL 。 只是文体的原因和一些边缘情况。

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

我不认为我曾经见过这个:

 int* pVal='\0'; 

但这是相当普遍的:

 char a=NULL; 

但这不是好的forms。 它使得代码更加便于携带,在我看来,它的可读性更差。 这也可能会导致混合C / C ++环境中的问题。

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

 #define NULL 0 

其他人可能使用:

 #define NULL ((void*) 0) 

而且我看到其他人定义为一个整数,以及各种奇怪的处理。

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

这将使你的代码更加便携 – 如果你改变编译器/环境/编译器设置,你将不会获得关于types等的警告。 在C或混合C / C ++环境中,这可能更重要。

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

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

这相当于:

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

我们正在给一个char赋一个整数值。 这可能会导致编译器警告,如果有足够的事件发生,很快就看不到任何重要的警告。 对我而言,这是真正的问题。 在代码中有警告有两个副作用:

  1. 给予足够的警告,你不会发现新的。
  2. 它给出警告是可以接受的信号。

在这两种情况下,实际的错误都可以通过,如果我们不小心阅读警告(或打开 – 错误)

是的,在解决重载function时,他们可能会performance出不同的行为。

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

func(NULL)调用func(integer_type)


您可以通过使用nullptr (通常是指针types)来消除混淆,在分配/比较值或重载parsingfunction时不显示任何歧义。

 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 ++编程Stroustrup第4版的书:

在较旧的代码中,通常使用0或NULL来代替nullptr(第7.2.2节)。 但是,使用nullptr消除了整数(如0或NULL)和指针(如nullptr)之间的潜在混淆。


计算机程序有两种types的阅读器。

第一种types是计算机程序,就像编译器一样。

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

程序通常可以用一种types的零代替另一种。 也有例外,正如其他答案所指出的,但这并不重要。

重要的是,你正在搞乱读者。

人类的读者情境非常敏感。 通过使用错误的零,你是在撒谎给你的读者。 他们会诅咒你的。

一个撒谎的人可以更容易地忽略错误。
一个撒谎的人可以看到不存在的“虫子”。 当“修复”这些phanthom错误,他们引入了真正的错误。

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

摘自C ++ 14草案N3936:

18.2types[support.types]

3在这个国际标准(4.10)中,macrosNULL是一个实现定义的C ++空指针常量。

4.10指针转换[conv.ptr]

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

因此, NULL可以是值为零的任何整数字面值,或者types为std::nullptr_t的值,类似于nullptr ,而'\0'总是零个窄字符文字。

所以,一般来说不能互换,即使在指针环境中,你也看不到任何风格的差异。

一个例子是:

 #include <iostream> #include <typeinfo> int main() { std::cout << typeid('\0').name << '\n' << typeid(NULL).name << '\n' << typeid(nullptr).name << '\n'; } 

我们来定义C / C ++中的NULL是什么。

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

定义参考C:

一个空指针常量是一个整数常量expression式,它的计算结果为0(如0或0L),或者是types为void *(类似于(void *)0)的types转换。

定义参考C ++ 98:

空指针常量是一个求值为零的整型常量expression式(如0或0L)。

定义引用C ++ 11:

空指针常量可以是求值为零的整型常量expression式(如0或0L),也可以是types为nullptr_t(如nullptr)的值。

重载方法的例子。

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

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

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