NULL宏或NULL常量

16

我目前正在阅读一本关于C ++的书,作者在其中解释说最好使用常量而不是NULL宏,但没有详细说明原因或给出任何优缺点。

那么为什么最好使用常量:

const int NULL = 0;
int *ptr = NULL;

改为:

int *ptr = NULL;
唯一的解释是NULL宏不具有类型安全性。

30
使用 nullptr,根据现代 C++。 - Ajay
7
NULL 宏首先必须是一个宏。其次,它可以是被评估为零的常量右值、实际上的零或其他特定于平台的值,当转换为指针时为 null 指针,或者它可以是 nullptr_t prvalue(像 nullptr)。该宏通常定义为 0nullptr(取决于编译器使用的 C++ 规范)。使用变量没有意义,也会导致转换问题。 - Some programmer dude
5
只告诉你该做什么而不解释原因的书籍是相当无用的。 - Daniel Daranas
13
这本书真的告诉你要定义一个名为NULL的整数常量吗?如果是这样,请不要再使用它了。 - user2672107
6
@Simon那从来不是也永远不会是一个好建议。 - user2672107
显示剩余8条评论
4个回答

40

所有内容都已过时。

使用nullptr代替。它是指向任何东西的指针的特殊值,甚至还有自己的类型std::nullptr_t

不能保证地址0x0对应于“未初始化”的指针值,但请注意,字面量0保证转换为空指针值。


20
什么?字面上的 0 仍然保证会隐式转换为一个空指针。 - user743382
6
有一个保证,ptr=0;会将指针ptr赋予平台适当的空值。 - Chris Becke
4
只有在编译时常量表达式具有整数类型时才会生效(const int 全局对象并不是,即使它使用字面值 0 进行初始化也不是)。 - The Paramagnetic Croissant
4
两个方面都错了。在初始化程序可见的上下文中,“const int”全局对象是编译时常量表达式,可以在需要常量表达式的上下文中使用(例如模板参数),但尽管如此,它不是空指针常量,因为规则不再要求它是值为零的整数类型的编译时常量表达式,而是一个字面值“0”。 - user743382
2
@AndrewHenle 嘿,是的,标准对此非常明确。但这不是我想说的。我在谈论隐式转换和显式转换的区别。 :) - user743382
显示剩余7条评论

22
如果你正在使用C++11,则建议使用nullptr而不是NULL。
以下是Bjarne Stroustrup的《C++程序设计语言》中的几行:
  • 在旧代码中,通常使用0或NULL代替nullptr(§7.2.2)。但是,使用nullptr可以消除整数(例如0或NULL)和指针(例如nullptr)之间的潜在混淆。
  • 在不同实现中,NULL的定义存在差异;例如,NULL可能是0或0L。在C中,NULL通常为(void∗)0,这在C++中是非法的(§7.2.1):
  • 使用nullptr比其他替代方案使代码更易读,并避免了当函数被重载以接受指针或整数时可能出现的混淆
希望这可以帮助您理解。

1
作者说得没错,预处理器宏没有类型(与变量相反,在其声明期间强制执行类型)。因此,在这方面,宏更加危险,因为编译器无法验证表达式中的类型正确性,而且在编译期间可能会错过重要的警告/错误消息。

当然,编译器创建者可以(并通常会,我希望如此)提供一个NULL宏的版本,该宏扩展为具有强制转换的值,等效于C定义:
#define N ((void*)0)

顺便提一下,自从C++11以来,NULL可以评估为std :: nullptr_t类型。

因此,我不会对NULL抱有太多问题,但您可能考虑避免使用自己的宏。或者至少在您不完全确定您的宏可能在各种表达式中被扩展的许多上下文中时,谨慎使用您的宏。

作为一个简短的练习,您可以尝试下面的内容,以查看宏没有类型,因此它们的类型在表达式中被推断,受例如算术转换或提升等影响。在下面的示例中,MY_NULL宏(=无需转换)导致相当危险的分配。这就是您的书籍作者所想并试图警告您的内容。

MY_TYPED宏评估为转换后的表达式,这确保编译器在尝试例如i = MY_TYPED_NULL;时捕获错误。

#define MY_NULL 0
#define MY_TYPED_NULL ((void*)0)
int i;
float f;
void* v;
i = MY_NULL;    // bad: no compiler error
f = MY_NULL;    // bad: no compiler error
v = MY_NULL;    // seems to match programmer's intention
i = MY_TYPED_NULL;    // good: compiler error
f = MY_TYPED_NULL;    // good: compiler error
v = MY_TYPED_NULL;    // correct

1
我不知道为什么人们一直这样说。当然,像NULL这样的东西有一个类型(它是因为其他原因而丑陋)。只有宏参数可能会有问题。 - MikeMB
1
在C ++中,NULL始终只是0,因为这就是它在C ++中的工作方式。 - user2672107
@Artur Opalinski,所评估的 NULL 宏的类型是整数,不是任何指针类型。这就是为什么它被滥用以初始化整数为 0 的原因,因此今天无法将 NULL 定义为 nullptr。 - user2672107
1
那么,我不确定你的练习应该证明什么。问题不在于NULL(或者它所评估出的表达式)没有类型,而是标准允许从整型字面量'0'(具有类型)到指针类型的隐式转换。我并不是说当有更好的选择时使用宏是个好主意。只是你的例子没有表明NULL没有类型。但我认为我误解了你最初的陈述的含义。 - MikeMB
1
引用标准(N3242):“在本国际标准中,宏NULL是一个实现定义的C++空指针常量(4.10)。可能的定义包括0和0L,但不包括(void*)0。” - user2672107
显示剩余4条评论

1

事实上,“NULL宏”不具备类型安全性。这意味着您的编译器不知道您是否使用了正确的类型。例如,在使用memcpy时:

SomeClass a;
AnotherClass b;
memcpy((void*)&a, (void*)&b, sizeof(b));

(摘自这里)

编译器只在内存中看到两个指针。但是SomeClassAnotherClass都是不完整的类型。

正如其他人所说,如果您可以使用C++11,只需使用nullptr即可。


3
这与NULL无关,而是与不安全的void*通用指针类型有关。此外,在C++中使用memcpy通常不是一个好主意。 - Some programmer dude

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接