'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
会喜欢那个接受char
types的参数。 但是,重要的是要注意,在典型的编译器中, NULL
更喜欢使用int
types的参数,而不是void*
types的重载。
可能导致这种混淆的是C语言允许将NULL
定义为(void*)0
。 C ++标准明确规定(草案N3936,第444页) :
macros
NULL
可能定义包括0
和0L
,但不包括(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中的nullptr
replace。
如果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不同,当你用\0
和NULL
常量调用它们时,你会注意到它们之间的区别。 但是,只要将这些常量放入variables中,差异就会消失,因为调用的函数将从variables的types中减去。
使用正确的常量可以使代码更易于维护,更好地传达意义。 你应该使用0
表示数字, \0
表示一个字符,而nullptr
表示一个指针。 Matthieu M.在评论中指出, GCC有一个错误 ,其中char*
与\0
进行比较,而目的是取消引用指针并将一个char
与\0
进行比较。 如果在代码库中使用正确的样式,这样的错误更容易被检测到。
要回答你的问题,并没有一个实际的用例可以防止你使用\0
和NULL
。 只是文体的原因和一些边缘情况。
请不要这样做。 这是一个反模式,实际上是错误的。 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赋一个整数值。 这可能会导致编译器警告,如果有足够的事件发生,很快就看不到任何重要的警告。 对我而言,这是真正的问题。 在代码中有警告有两个副作用:
- 给予足够的警告,你不会发现新的。
- 它给出警告是可以接受的信号。
在这两种情况下,实际的错误都可以通过,如果我们不小心阅读警告(或打开 – 错误)
是的,在解决重载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)中,macros
NULL
是一个实现定义的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); }
使用参数NULL
或nullptr
调用method1(void* arg0);
应调用method1(void* arg0);
。 但是如果我们用参数'\0'
调用method1或者0
应该执行method1(char arg0);
和method1(int arg0);
。