何时使用 NULL 以及何时在 C 的链表中使用 '\0'?

When to use NULL and when to use '\0' in linked list in C?

我在C里学的:null char == '[=12=]' == NULL,下面写了一个循环,在C里从头到尾读取一个char[]

// case #1
char buf[32];
while (buf[i] != NULL){
    //do something...
}  

但是,我的 gcc 编译器给了我一个警告:指针和整数之间的比较。有人提到我混淆了两个不同的概念:NULL 用于指针,而 '\0' 用于字符。因此,为了消除警告,我应该使用“\0”,因为我的循环测试的是一个字符。

现在我正在写一个链表,测试头指针是否指向节点。因为它是结构,所以使用 if (h1 == NULL) 是合理的,但显然当我使用 if (h1 == '[=14=]') 时编译器也会编译,即使节点是结构而不是字符。有人可以提供一些帮助,为什么在这种情况下可以同时使用 '\0' 和 NULL 而在第一种情况下不能同时使用它们?

// case #2
struct ListNode {
    int val;
    struct ListNode *next;
};

根据编译器的不同,NULL 可能有不同的定义方式:

#define NULL ((char *)0)

#define NULL ((void *)0)

#define NULL 0L

#define NULL 0

NULL 用于指针比较。但是,整数 0 是允许进行指针比较的特殊情况,这就是为什么您可以直接将 ListNode* 指针与 0 进行比较(否则上面的 NULL 定义了哪里不存在指针类型转换将不起作用):

if (h1 == 0)

或更简单:

if (!h1)

'[=24=]' 字符文字在 C 中是 int,在 C++ 中可隐式转换为 int。所以'[=24=]'也可以用在指针比较中。

您应该使用 '[=24=]'(或只是 0)进行字符比较,不要使用 NULL:

while (buf[i] != '[=16=]') // or 0

或更简单:

while (buf[i])

NULL 有效地用作链表或指针数组的空终止符。 '[=12=]' 用作字符串的空终止符(即 char 数组)

如果您有一个结构数组,您可能希望遍历它们直到到达末尾:

MyStruct** things = ...
for(int i = 0; things[i] != NULL; i++) {
    // Do something with things[i]
}

当指向最后一个结构的指针为NULL时,此循环将结束。

这是一个很常见的混淆。 "null character"(出于历史原因,通常拼写为 NUL,只有一个 L)是一个 字符,比较等于零。 "null pointer" 是比较等于零的 指针 。区别仅在于类型——但这是一个重要的区别。允许编译器反对您的程序,但不是必需的,因为您正在将 character 值与 null pointer 常量进行比较。

C 有一个相当松散的类型系统,尤其是涉及到整型常量时,因此您可以逃避 很多时候使用形式上不正确类型的常量。当涉及到零时尤其如此:整数文字 0 可以在任何可以安全使用宏 NULL 或字符文字 '[=12=]' 的地方使用。事实上,在 C 标准的所有版本中都明确允许实现使用 #define NULL 0。因此,有一小部分上下文绝对必须对某些具体类型 T 使用 (T*)0 而不是 0NULL,例如将空指针传递给参数数量可变的函数(例如 execl)。

作为混淆蛋糕上的进一步锦上添花,在 C 中,字符文字的类型为 int,而不是 char,并且 '[=12=]' 是一个合法的 空指针常量。 (在 C++ 中,这些都不是真的。)

C 中的最佳实践,恕我直言,是使用 '[=12=]' 作为空 字符 ,并且 0not NULL — 对于 null pointer.