NULL、'\0'和0之间有什么区别?

381

在C语言中,似乎存在着零的不同取值 -- NULLNUL0

我知道ASCII字符'0'的值为480x30

NULL指针通常被定义为:

#define NULL 0

或者

#define NULL (void *)0

此外,还有一个NUL字符'\0',它似乎也会被解释为0

这三个值有时可能不相等吗?

在64位系统上,这也是正确的吗?


2
请参考https://dev59.com/_XVC5IYBdhLWcg3w0EkD#1234382,了解0和NULL之间的区别。 - David Rodríguez - dribeas
14
标识符NUL在C标准语言或库中不存在(至少在我所知道的C++中也是如此)。空字符有时被称为NUL,但在C或C++中通常只被称为'\0' - Keith Thompson
11个回答

425

注意:本回答适用于 C 语言,而非 C++。


空指针

整数常量字面值 0 根据使用上下文的不同含义也会不同。在所有情况下,它仍然是一个整数常量,其值为 0,只是描述方式不同。

如果将一个指针与常量字面值 0 进行比较,则是检查该指针是否为空指针。这个 0 被称为空指针常量。C 标准定义了将 0 强制转换为类型 void * 既是空指针又是空指针常量。

此外,为了提高可读性,在头文件 stddef.h 中提供了宏 NULL。根据编译器的不同,可能可以使用 #undef NULL 取消定义并重新定义为其他奇怪的东西。

因此,以下是一些有效的检查空指针的方式:

if (pointer == NULL)

NULL 被定义为与空指针相等。实际上,NULL 的定义是由具体的实现决定的,只要它是一个有效的空指针常量即可。

if (pointer == 0)

0 是代表空指针常量的另一种表示方法。

if (!pointer)

这个if语句隐式地检查“不是0”,所以我们将其反转为意味着“是0”。

以下是无效的检查空指针的方法:

int mynull = 0;
<some code>
if (pointer == mynull)

对于编译器来说,这并不是一个空指针的检查,而是两个变量的相等性检查。如果mynull在代码中永远不会改变并且编译器优化将0折叠到if语句中,那么这可能有效,但这并不是保证的。根据C标准,编译器必须产生至少一条诊断消息(警告或错误)。

请注意,在C语言中,空指针的值在底层架构上并不重要。如果底层架构将空指针值定义为地址0xDEADBEEF,则由编译器解决这个问题。

因此,即使在这个有趣的架构上,以下方式仍然是检查空指针的有效方式:

if (!pointer)
if (pointer == NULL)
if (pointer == 0)
以下是检查空指针的错误方式:
#define MYNULL (void *) 0xDEADBEEF
if (pointer == MYNULL)
if (pointer == 0xDEADBEEF)

因为编译器将它们看作普通的比较,所以这些语句会被认为是正常的比较。

空字符

'\0' 被定义为空字符,也就是所有位都设置为零的字符。 '\0' (像所有字符字面量一样)是一个整数常量,具有值零。 因此,'\0' 完全等同于未装饰的 0 整数常量-唯一的区别在于它传达给人类读者的意图(“我正在使用这个作为空字符。”)。

'\0' 和指针无关。但是,您可能会看到类似以下代码:

if (!*char_pointer)

检查字符指针是否指向空字符。

if (*char_pointer)

检查字符指针是否指向非空字符。

不要将其与空指针混淆。尽管它们的位表示相同,这样可以方便地进行一些交叉操作,但它们实际上并不是相同的东西。

参考资料

有关更多信息,请参见comp.lang.c FAQ的问题5.3。 请参阅此pdf文件以获取C标准。查看第6.3.2.3节指针,第3段。


3
感谢指出FAQ列表。但是,也请参阅http://c-faq.com/null/nullor0.html。 - Sinan Ünür
4
不,你不会将“ptr”与全零位进行比较。这不是一个“memcmp”,而是使用内置运算符进行的比较。一侧是空指针常量“'\0'”,另一侧是指针。同样适用于使用“NULL”和“0”的其他两个版本。这三个版本执行相同的操作。 - Johannes Schaub - litb
7
你将内置比较运算符视为一种比较位串的工具。但实际上它用于比较抽象概念下的两个值。所以,一个在内部以0xDEADBEEF表示的空指针仍然是空指针,无论其位串长什么样子,它仍将等于 NULL0\0 和所有其他空指针常量形式。 - Johannes Schaub - litb
2
你就比较运算符提出了一个很好的观点。我复习了C99,它说:“具有值为0的整数常量表达式或将这样的表达式强制转换为void*类型的表达式称为零指针常量。” 它还说字符字面量是整数常量表达式。 因此,根据传递性,你是对的,即ptr == '\0' - Andrew Keeton
2
“...可能可以使用 #undef NULL 并将其重新定义为一些古怪的东西。任何这样做的人都应该被枪毙。” 这位先生说得好,这让我大声笑了出来... - oggiemc
显示剩余18条评论

43

似乎有许多人误解了NULL、'\0'和0之间的区别。因此,为了说明问题并避免重复,以下是详细说明:

类型为int、值为0的常量表达式,或者强制转换为void *类型的该类型表达式是空指针常量,如果转换为指针,则成为空指针。 标准保证它与任何对象或函数的指针比较时不等

NULL是一个宏,定义为空指针常量

\0是用于表示空字符的构造,用于终止字符串。

空字符是一个字节,其所有位均设置为0。


2
你漏掉了 0 - Pacerier

18

这三个概念定义了0在不同上下文中的含义。

  • 指针上下文——使用NULL表示一个指针的值为0,无论是32位还是64位(一个情况下是4个0字节,另一个情况下是8个0字节)。
  • 字符串上下文——表示数字0的字符具有0x30的十六进制值,而NUL字符具有0x00的十六进制值(用于终止字符串)。

当你查看内存时,这三个始终是不同的:

NULL - 0x00000000 or 0x00000000'00000000 (32 vs 64 bit)
NUL - 0x00 or 0x0000 (ascii vs 2byte unicode)
'0' - 0x20
我希望这样能澄清它。

9
Nasko:评估 sizeof('\0'),并感到惊讶。 - caf
3
@Nasko: 我很惊讶:在C语言中使用gcc编译器时,sizeof('\0') == sizeof('a') == 4,但是在C++中使用g++编译器时,sizeof('\0') == sizeof('a') == 1。请你翻译这段话。 - David Rodríguez - dribeas
1
@Nasko:根据C标准(草案,n1124):“整数字符常量的类型为int”,因此在C中'\0'实际上是int类型,因此在我的架构(linux,32位)中sizeof('\0')为4。 - David Rodríguez - dribeas
@dribeas - 我并没有将它描述为常量,而是作为字符串的一部分。我肯定可以更明确地表达。谢谢。 - Nasko
@DavidRodríguez-dribeas 撤销了“将'0'的ASCII值更正为0x20(十进制32)”的编辑。 - chux - Reinstate Monica

13

如果在C语言中,NULL和0作为空指针常量是等价的,那我应该使用哪个? C FAQ列表中也解决了这个问题:

C程序员必须理解,在指针上下文中,NULL0是可以互换的,并且未经转换的0是完全可以接受的。除非需要提醒指针参与其中,否则应将任何对NULL(而不是0)的使用视为温和的提醒。程序员不应该依赖它(无论是为了自己的理解还是为了编译器的理解)来区分指针0和整数0

只有在指针上下文中,NULL0才是等价的。当需要其他类型的0时,不应使用NULL,即使它可能起作用,因为这样做会发送错误的风格信息。(此外,ANSI允许定义NULL((void *) 0),这在非指针上下文中根本不起作用。)特别是当需要ASCII空字符(NUL)时,不要使用NULL。请提供自己的定义。

#define NUL '\0'

如果你确实必须这样做。


7
“NULL”、“\0”和“0”的区别是什么?
“null character (NUL)”最容易被排除掉。“\0”是一个字符字面量。在C语言中,它被实现为“int”,因此与0相同,而0是“INT_TYPE_SIZE”的类型。在C++中,字符字面量被实现为“char”,大小为1个字节。这通常与“NULL”或“0”不同。
其次,“NULL”是指定变量不指向任何地址空间的指针值。抛开通常被实现为零的事实,它必须能够表达架构的完整地址空间。因此,在32位架构上,“NULL”(可能)是4个字节,在64位架构上是8个字节。这取决于C的实现。
最后,字面量“0”是类型为“int”的,大小为“INT_TYPE_SIZE”。默认值可能因架构而异。
Apple写道:
Mac OS X使用的64位数据模型被称为“LP64”。这是其他来自Sun和SGI以及64位Linux的64位UNIX系统所使用的常见数据模型。LP64数据模型将原始类型定义如下: - int为32位 - long为64位 - long-long也为64位 - 指针为64位
维基百科64-bit: Microsoft的VC++编译器使用LLP64模型。
64-bit data models
Data model short int long  long long pointers Sample operating systems
LLP64      16    32  32    64        64       Microsoft Win64 (X64/IA64)
LP64       16    32  64    64        64       Most Unix and Unix-like systems (Solaris, Linux, etc.)
ILP64      16    64  64    64        64       HAL
SILP64     64    64  64    64        64       ?

编辑:添加了更多关于字符字面量的内容。

#include <stdio.h>

int main(void) {
    printf("%d", sizeof('\0'));
    return 0;
}

上述代码在gcc上返回4,在g++上返回1。

2
不,'\0' 不是一个 1 字节的值。它是一个字符字面量,是一个整数常量表达式 - 因此如果可以说它有一个大小,那么它就是 int 的大小(它必须至少为 2 个字节)。如果你不相信我,请计算 sizeof('\0') 并自行查看。'\0'00x0 都是完全等价的。 - caf
@caf 这取决于编程语言。如果你不相信我,可以在 C++ 编译器上尝试 sizeof('\0') - Eugene Yokota
2
在打印某个变量的大小时,应该使用“%zu”。 - Unused
在C语言中,由于自动转换默认为int类型,因此在计算机上的值为4。如果将该值分配给char、int或long long int类型而不进行重新转换,则其大小将相应更改。此外,变量'a'的大小为4。 - user17492848

5

有一篇很好的文章,对于刚开始学习C语言的人很有帮助(来自Linden所写的《专家C编程》)

单个'l'空字符和双个'l'空指针

记住这个小押韵,以便正确使用指针和ASCII零:

The one "l" NUL ends an ASCII string,

The two "l" NULL points to no thing.

Apologies to Ogden Nash, but the three "l" nulll means check your spelling. 
  • 二进制位模式为零的ASCII字符被称为“NUL”。
  • 指针指向无处的特殊指针值是“NULL”。
  • 这两个术语在含义上不能互换。

更简单的解释:NUL是一个控制码,例如BELVTHTSOT等,因此最多只有3个字符。 - glglgl

4

一个L的NUL,它结束一个字符串。

两个L的NULL指向无任何东西。

我敢打赌一只金牛

不存在三个L的NULLL。

如何处理NUL?


3
"NUL"并不等于0,而是指ASCII码中的NUL字符。至少,在我看到过的情况下是这样使用的。空指针通常被定义为0,但这取决于你正在运行的环境,以及所使用的操作系统或语言的规范。
在ANSI C中,空指针被指定为整数值0。因此,任何不符合此规范的环境都无法符合ANSI C标准。

3
一个值为0x00的字节,在ASCII表中,是特殊字符NULNULL。在C语言中,由于不应该在源代码中嵌入控制字符,因此用转义的0来表示,即\0

但真正的NULL不是一个值,它是缺少值。对于指针而言,它意味着指针没有任何指向。在数据库中,它意味着字段中没有值(这并不意味着该字段为空白、为0或填充了空格)。

给定系统或数据库文件格式用于表示NULL的实际值不一定是0x00


1

NULL不能保证为0,其确切值取决于架构。大多数主要的架构将其定义为(void*)0

'\0'始终等于0,因为字符字面量中的第0个字节是如何编码的。

我不记得C编译器是否需要使用ASCII - 如果不是,则'0'可能不总是等于48。无论如何,除非您在非常晦涩的系统上工作,否则不太可能遇到使用EBCDIC等替代字符集的系统。

各种类型的大小在64位系统上会有所不同,但整数值将相同。


一些评论者表示怀疑NULL是否等于0,但不是零。这里有一个示例程序,以及在这样的系统上预期的输出:
#include <stdio.h>

int main () {
    size_t ii;
    int *ptr = NULL;
    unsigned long *null_value = (unsigned long *)&ptr;
    if (NULL == 0) {
        printf ("NULL == 0\n"); }
    printf ("NULL = 0x");
    for (ii = 0; ii < sizeof (ptr); ii++) {
        printf ("%02X", null_value[ii]); }
    printf ("\n");
    return 0;
}

那个程序可以打印:

NULL == 0
NULL = 0x00000001

2
OP问的是'\0'(NUL字符),而不是'0'(零字符)。 - Chris Lutz
2
@Chris: '\0' 不是 NULL,它是一个以八进制编码的字节 0 转换成字符文字。 - John Millikin
2
在C++中,标准保证将整数值0转换为指针始终会产生空指针。在C++中,0被保证为null指针,而另一方面,NULL是一个宏,恶意编码者可以重新定义它为不同的东西。 - David Rodríguez - dribeas
6
NULL 保证为 0。NULL 指针的位模式不保证全部为零,但是 NULL 常量保证始终为 0。请注意,此处不提供解释。 - jalf
2
您的第一句话是错误的 - 在C ++中,无法将NULL定义为(void *)0,因为没有从void *到另一个指针的隐式转换(与C不同)。 - anon
显示剩余16条评论

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