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
无法互换的示例(最好是实际申请而不是病态案例)?
答案 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)
Leon is correct当同一个函数有多个重载时,\0
更喜欢带有char
类型参数的重载。但是,重要的是要注意在典型的编译器上,NULL
更喜欢带有int
类型参数的重载,而不是void*
类型的参数!
可能导致这种混淆的原因是C语言允许将NULL
定义为(void*)0
。 C ++标准明确声明 (草案N3936,第444页) :
宏[{1}}]的可能定义包括
NULL
和0
,但不包括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
替换。
另一个问题的另一个病态案例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
;当你的意思是一个指针时,你应该使用\0
。 Matthieu M.在评论中指出GCC had a bug,其中nullptr
与char*
进行了比较,而目的是取消引用指针并比较\0
到char
。如果在代码库中使用了合适的样式,则更容易检测到这些错误。
要回答您的问题,实际上并没有实际的用例会阻止您互换使用\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分配一个整数值。这可能会导致编译器警告,如果有足够多的情况,很快您就看不到任何重要的警告。对我来说,这是真正的问题。代码中有警告有两个副作用:
在这两种情况下,实际的错误都可以通过,如果我们打扰阅读警告(或打开-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:
的定义空指针常量是一个整数常量表达式 计算结果为零(如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);
}
使用参数NULL
或nullptr
调用method1应调用method1(void* arg0);
。但是,如果我们使用参数'\0'
或0
调用method1,则应执行method1(char arg0);
和method1(int arg0);
。