为什么在C和C ++中对NULL指针的定义不同?


73

在CNULL中定义为,(void *)0而在C ++中定义为0。为什么会这样呢?在CI中可以理解,如果NULL不进行类型转换,(void *)则编译器可能会/可能不会生成警告。除此之外,还有什么原因吗?


7
一个有趣的区别是,在C中,您可以隐式转换void*为任何指针类型,而在C ++中,则不能。
asveikau 2011年

2
C或C ++的实现都可以合法地定义NULL0。AC实现可以将其定义为((void*)0),但不能定义为(void*)0。标准C标头中定义的宏必须用括号保护,以便可以像使用单个标识符一样使用它。使用#define NULL (void*)0,该表达式sizeof NULL将是语法错误。
基思·汤普森

Answers:


103

回到C ++ 03,ISO规范(§4.10/ 1)将空指针定义为

空指针常量是整数类型的整数常量表达式(5.19)rvalue,其值为零。

这就是为什么在C ++中您可以编写

int* ptr = 0;

在C中,此规则相似,但有些不同(第6.3.2.3/3节):

值为0的整数常量表达式,或将这种类型强制转换为type的表达式 void *称为空指针常量。55)如果将空指针常量转换为指针类型,则可以保证所得的指针称为空指针。比较不等于指向任何对象或函数的指针。

因此,两者

int* ptr = 0;

int* ptr = (void *)0

是合法的。但是,我的猜测是void*演员表在这里,这样的语句

int x = NULL;

在大多数系统上产生编译器警告。在C ++中,这是不合法的,因为您不能在没有强制转换的void*情况下将a隐式转换为另一种指针类型。例如,这是非法的:

int* ptr = (void*)0; // Legal C, illegal C++

但是,这会导致问题,因为代码

int x = NULL;

是合法的C ++。因此,随之而来的混乱(还有另一种情况,稍后显示),自C ++ 11起,存在一个nullptr表示空指针的关键字:

int* ptr = nullptr;

这没有上述任何问题。

nullptr大于0的另一个优点是,它在C ++类型系统中的播放效果更好。例如,假设我具有以下两个功能:

void DoSomething(int x);
void DoSomething(char* x);

如果我打电话

DoSomething(NULL);

相当于

DoSomething(0);

调用DoSomething(int)而不是预期的DoSomething(char*)。但是,有了nullptr,我可以写

DoSomething(nullptr);

然后它将DoSomething(char*)按预期方式调用该函数。

同样,假设我有一个,vector<Object*>并且想要将每个元素设置为空指针。使用std::fill算法,我可能会尝试编写

std::fill(v.begin(), v.end(), NULL);

但是,这不能编译,因为模板系统将其NULL视为anint而不是指针。为了解决这个问题,我必须写

std::fill(v.begin(), v.end(), (Object*)NULL);

这是丑陋的,并且在一定程度上破坏了模板系统的目的。要解决此问题,我可以使用nullptr

std::fill(v.begin(), v.end(), nullptr);

并且由于nullptr已知其类型与空指针相对应(具体来说,std::nullptr_t),因此可以正确编译。

希望这可以帮助!


3
看看@Keith Thompsons的答案。C中的“空指针常量”是任意0可选值转换为的任何常量整数表达式void*
詹斯·古斯特

@Jens Gustedt-您能查看我编辑后的答案是否更正确?
templatetypedef

3
几乎完美!只是一个建议,您似乎仍然暗示NULL必须(void*)0在C中。事实并非如此。不幸的是,它只能定义为任何空指针常量,因此在C语言中并不是一个非常可靠的概念。因此,我个人认为NULL应该避免这样做。有些人似乎更喜欢它是指针类型的原因,更多是将隐式参数提升为没有原型的函数的规则,即va_arg参数列表。但这是另一个故事...
Jens Gustedt 2011年

17

在C中,NULL扩展为实现定义的“空指针常量”。空指针常量可以是值为0的整数常量表达式,也可以是强制转换为的表达式void*。因此,C实现可以将NULLas0或as定义为((void*)0)

在C ++中,空指针常量的规则不同。特别地,((void*)0)它不是C ++空指针常量,因此C ++实现无法以NULL这种方式定义。


-6

创建C语言是为了简化对微处理器的编程。AC指针用于将数据地址存储在内存中。需要一种方法来表示指针没有有效值。由于所有微处理器都使用该地址进行引导,因此选择了地址零。因为它不能用于其他任何事情,所以零表示一个没有有效值的指针是一个不错的选择。C ++向后兼容C,因此它继承了该约定。

用作指针时强制转换零的要求只是最近的补充。后来的C语言希望更加严格(希望错误更少),因此他们开始对语法更加学究。


2
从哪里得到的要求转换0为指针类型。C和C ++都没有这样的规则。在C语言中,您可能具有(void*)0一个空指针常量,但是没有什么强迫您这样做。
詹斯·古斯特

3
我认为举证责任是相反的-标准在任何地方都没有说空指针必须具有任何特定的表示形式。搜索“空指针表示”,您将看到它讨论了无数次。请注意,0source中的常量必须是一个空指针常量-但这并不要求空指针的内部表示形式必须全部为零,也不必与整数零的表示形式相同。
ymett 2011年

2
指针以某种方式在内存中表示(除非编译器对其进行了优化)。此表示形式在源代码中不存在。因此,肯定存在“一些隐藏的内部表示形式”。普通表示形式只是所指向对象的地址。对于空指针,这显然不起作用,因为没有指向的对象,因此编译器必须使用其他对象。通常使用所有零位,但是标准在任何地方都不需要此位(并且已经有编译器使用其他位)。
ymett

3
@Jay:我使用的系统将值1用作空指针,因为CPU会捕获对奇数地址的引用。诚然,这不是C编译器,但对空指针使用非零表示形式确实有好处。我们都不知道未来的系统会是什么样子,但是我们已经看到由于类似的更改(例如,依赖于的代码)而需要大量重写*(int*)0 == 0。编写不假定空指针为全零位的代码确实没有那么困难。
基思·汤普森

2
@Jay:您可以选择。您可以编写在空指针不是全零位的系统上失败的代码;该代码可能会在大多数系统上正常工作。或者,您可以编写不假设空指针如何表示的代码。该代码将在所有系统上正常工作。在后一种情况下,当(如果不是)您的程序行为不正常时,您不必担心太多原因。这是有趣的部分:第二种选择并不比第一种困难。
Keith Thompson
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.