如何使用 #define 定义 NULL

9
我想重新定义程序中的NULL,例如:
#define MYNULL ((void*)0)

但是在以下语句中,这个定义并不起作用:
char *ch = MYNULL;

错误:无法将void*转换为char*

如何最好地定义NULL?


12
哪种语言?是C还是C++,它们是非常不同的语言。 - Binary Worrier
16
为什么您认为需要这样做? - anon
你能把变量ch的名称改成其他的吗(比如str)?因为它很容易让读者混淆,他们可能会错误地认为你对NUL处理感兴趣。但我的真正问题是(就像@Neil Butterworth问的那样),为什么要这样做?(或者你能澄清一下你希望实现什么吗?你是想让一个单一的显式常量同时扮演指针和NUL字符的双重角色吗?) - Ruben Bartelink
4
目前来看,这只是一个糟糕的问题,因为你几乎肯定不应该这样做。也许如果你解释一下你的动机,这个理由可能会更清楚? - Draemon
@Draemon:6个赞表明这是一个很棒的问题 :P(我刚刚才加了我的-1... @cppdev:不要忘记回复所有这个问题引发的跟进!) - Ruben Bartelink
4
这是一个令人担忧的问题——表明正在做一些不好的事情。 - JoeG
8个回答

28
#define MYNULL NULL

这是最安全的方法,我认为没有必要这样做,但如果你真的想这样做,可以这样操作。 下面分别是 C 和 C++ 的方法:

#define NULL 0 //C++
#define NULL ((void*)0) //C

通常情况下,将0定义为NULL是一种不好的习惯,你实际上希望它成为语言的一部分。C++0x 解决了这个问题。

Bjarne Stroustrup 对此有以下看法

我应该使用 NULL 还是 0?

在 C++ 中,NULL 的定义为 0,因此只有美学上的区别。我喜欢避免使用宏,所以我使用 0。另一个使用 NULL 的问题是,人们有时会错误地认为它与 0 不同和/或不是整数。在早期标准之前的代码中,NULL 有时被定义为某些不适当的内容,因此必须避免使用。但现在这种情况已经不太常见。

如果你必须给空指针命名,就叫它nullptr;这就是 C++11 中它的名称。然后,“nullptr” 将成为关键字。


18
#ifdef __cplusplus
#define MYNULL 0
#else
#define MYNULL ((void*)0)
#endif

它们两个都能起作用。


9
问题是如何在C++和C中使用#define来定义NULL,据我所了解,这不是关于“如果我等到某个人很好地实现了c++0x应该怎么处理NULL”或者“使用NULL有哪些注意事项”的问题。为什么不假设cppdev确切知道自己在做什么,只想得到这个问题的答案呢?这就是我讨厌stackoverflow的原因 - 通常我有使用我的解决方案的理由,并且不希望得到如何以其他方式解决它的提示。(好吧,这个问题没有前提可以假设这一点)。请将您的魔法球和其他预知未来的配件用于更有趣的事情上。 - nothrow
2
@Yossarian:一个好问题(值得许多赞)的一部分是要进行FizzBin测试和/或其他方式来确定您在决定向LazyWeb提出重要问题之前已经尝试了什么。不用担心成为唯一一个被SO及其众多+和(不太多的)-点(在各种意义上)激怒的人:P但是当我们在纠结细节时,您真的认为相对于站点上的所有其他答案,这个答案独特地值得96个声望?(不,我的答案不值60——它是评论的副本)。加油Johannnes! - Ruben Bartelink
1
我不介意负面分数,但我不喜欢你的论证方式。 - nothrow
3
与其得到一个负一评价,仅附带评论,还不如得到某人愿意坚守自己的观点并/或解释为什么他们认为自己的观点正确的评价,并使内容更加通俗易懂但不改变原来的意思。对于像这个问题这样微妙的问题,让群众根据可能存在缺陷的理解来决定事情的结果,通过使用基于不完整理解的赞成票来抵消反对票是不够好的。我必须说,在我看过的其他问题中,与此相比,在此问题标签上发生这种无聊的事情要多得多。就像我说的,我们都讨厌SO :( - Ruben Bartelink
1
我不确定你是否曾离开过x86 / x64平台,但有很多嵌入式系统没有任何标准库(但具有可工作的C / C ++编译器),你的无知并不是任何有价值的论据。 - nothrow
显示剩余16条评论

10

你从预期的地方获取 NULL 的问题究竟出在哪里?即,

#include <stddef.h>
或者
 #include <cstddef>

正如@Johannes Rudolph的答案中所提到的,你所做的任何诡计都不太可能在面对像nullptr等内容时具有很好的未来性。

编辑:虽然stdlib(和许多其他库)被要求包括NULL,但是stddef是最经典的头文件[并且已经是几十年了]。

PS:通常情况下,涉及此类技巧都不是一个好主意,除非你有一个真正的好理由。你没有详细说明导致你感觉需要这样做的思路。如果你能添加一些细节,那么可能会得到更好的答案。其他回答问题的人也应该指出这点,但我猜FGITW最擅长的就是这个:D

编辑2:正如@Yossarian所指出的那样:唯一使用这种技巧的理由是如果在您的系统中没有以适当的语言无关形式定义NULL。裸编译器没有头文件和/或如果您正在从头开始编写自己的自定义标准库,则是这种情况的示例。 (在这种基本情况下,我会选择@lilburne的答案(尽量使用0))


@legends2k: 对我来说(以及问题的评论中的 @Neil Butterworth,但在我之前没有人 +1),这似乎是显而易见的,但我猜人们喜欢解决不必要的谜题。 - Ruben Bartelink
@legends2k:你是说“黑客”吧!我猜人们来这个网站不是因为他们相信地球上需要说的一切都已经被说过了:P - Ruben Bartelink
@Ruben:嗯,“黑客”,但不是所有的人,尽管很多人是 :) - legends2k
1
@-1er:能否解释一下你的负评?如果你有理由认为这是一个错误的回答,分享推理将有益于世界。(如果你愿意,我可以提供一堆关于神秘选民身份的猜测和假设!) - Ruben Bartelink
例如,我需要在一个不允许使用标准库(实际上是任何库)的任务中定义null。 - Tomáš Zato
显示剩余8条评论

2
#define MYNULL 0

将会在C++中工作。


2
这仅适用于C ++。 C需要将NULL定义为(void *)0。 - Paul R
1
@Paul:在C语言中,将NULL定义为0是可以的,只是会稍微降低类型安全性。然而,在C语言中正确使用0作为NULL不会产生任何错误。如果你要反驳可变参数的哨兵值,建议即使使用NULL也要转换为char*类型。 - Evan Teran
@Evan - 你是对的 - 我检查了 - 谢谢你纠正我! - Paul R

2
不要这样做。 没有规定 NULL 必须是零,它是与实现相关的。 它可以是一个表示内存结束的值,内存中的某个特殊位置,甚至是表示不存在任何值的对象。
这样做非常危险,可能会破坏可移植性,并且肯定会影响代码感知编辑器。这不会为您带来任何好处,请相信您的库定义。
编辑:Evan 是正确的! 代码本身将显示为零,在底层编译器可以对实现特定的细节进行操作。谢谢 Evan!

2
这是对标准的常见误解。空指针常量0。然而,空指针值(在语言级别上不可见,因此不是您关心的问题)可能是机器特定的。C99标准6.3.2.3:“具有值为0的整数常量表达式或将这样的表达式强制转换为void *类型的表达式称为空指针常量。55)如果将空指针常量转换为指针类型,则所得到的指针称为空指针,并保证与任何对象或函数的指针比较时都不相等。”基本上就是说“在指针上下文中使用0表示NULL”。 - Evan Teran
另请参见:https://dev59.com/50rSa4cB1Zd3GeqPTAgr#1642776 和 http://stackoverflow.com/questions/423823/whats-your-favorite-programmer-ignorance-pet-peeve/1331729#1331729 以及 http://c-faq.com/null/machnon0.html。 - Evan Teran
@Walt:没问题,我现在会撤销我的-1评分的 :-)。 - Evan Teran

1
我认为任何不知道在C/C++中将指针设置为0与将其设置为NULL、nullptr或任何其他等效项相同的人都不应该去碰代码。这种做法会影响代码的可读性。
char* ch = NULL

char* ch = 0;

是最小的。当涉及到表达式时,形式如下:

if (NULL == ch) {
}
if (0 == ch) {
}
if (nullptr == ch) {
}

没有更可读性高

if (!ch) {
}

@lilburne:在你的例子中引入nullptr会使事情变得更加混乱,而不是更清晰。nullptr有其目的,并不总是完全可替换NULL。但我同意一般观点,对于大多数用法,应该考虑优先选择0和隐式强制转换为int而不是NULL。(但是当它混淆这些问题时,我不会给它+1 :D) - Ruben Bartelink

0
#define NULL 0 //for C

这是C语言中的完美定义

例如:

char *ch = NULL ;
*ch++ ;// will cause error

由于在执行增量语句时,指针ch指向空值,因此导致错误。编译器通过查看LOOK-UP表中指针的值为0来知道这一点。

如果你尝试更新该指针,则实际上是更改了从0物理地址开始的CODE区域的内容。因此,在代码区域开始之前,页表的第一个条目保持为空。

到底是从你应该得到NULL的地方出现了什么问题呢?

#include <stddef.h>

或者

#include <cstddef>

正如@Johannes Rudolph的回答中所提到的,你所做的任何诡计都不太可能在nullptr等因素面前具有未来可靠性。

编辑:虽然stdlib(和许多其他库)被强制包含NULL,但stddef是最经典的头文件[已经有几十年了]。

PS通常情况下,除非你有一个非常好的理由,否则千万不要涉及这种诡计。你没有详细说明导致你感觉需要这样做的思考过程。如果你能添加一些细节,那么很可能会得到更好的答案。其他回答问题的人也应该在他们的回答中指出这一点,但我猜FGITW最擅长做什么:D

编辑2:正如@Yossarian指出的那样:唯一的理由是如果在你的系统中没有以适当的语言无关形式定义NULL。裸编译器没有头文件和/或如果你正在从头开始编写自己的自定义标准库,则是这种情况的示例。(在这种基本的情况下,我会选择@lilburne的答案(尽可能使用0))


0
与某些人在此处所述的相反,0 在 C 中是 NULL 的完全有效定义。因此,当您将 NULL 作为可变参数函数的参数时,必须小心,因为它可能会被误认为是整数值 0,从而导致不可移植性问题。

http://c-faq.com/null/null2.html

顺便说一句,comp.lang.c常见问题解答对于每个C程序员都是一个强烈推荐的阅读资料。例如在这里:

http://c-faq.com/null/null1.html

包含着那些几乎被遗忘的智慧珠玑,例如“如上所述,每种指针类型都有一个空指针,并且不同类型的空指针的内部值可能是不同的。” 这意味着calloc或memset不是指针的可移植初始化。


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