我可以使用 NULL 代替 0 的值吗?

Rob*_*rtS 77 c c++ null pointers language-lawyer

我可以使用NULL指针代替 的值0吗?

或者这样做有什么问题吗?


比如,例如:

int i = NULL;
Run Code Online (Sandbox Code Playgroud)

作为替代:

int i = 0;
Run Code Online (Sandbox Code Playgroud)

作为实验,我编译了以下代码:

#include <stdio.h>

int main(void)
{
    int i = NULL;
    printf("%d",i);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

0
Run Code Online (Sandbox Code Playgroud)

事实上,它给了我这个警告,这本身是完全正确的:

warning: initialization makes integer from pointer without a cast [-Wint-conversion] 
Run Code Online (Sandbox Code Playgroud)

但结果仍然是等价的。


  • 我是否会因此而进入“未定义行为”?
  • 可以这样使用NULL吗?
  • NULL在算术表达式中用作数值有什么问题吗?
  • 对于这种情况,C++ 中的结果和行为是什么?

我已阅读的答案是什么NULL之间的区别,“\ 0”和0大约之间的区别是什么NULL\0以及0是的,但我没有从那里简洁的信息,如果是很允许的,也是正确的使用NULL作为在赋值和其他算术运算中使用的值。

Joh*_*ger 84

我可以使用 NULL 指针代替 0 的值吗?

,这样做是不安全的。 NULL是一个空指针常量,它可以具有 type int,但更典型的是具有 type void *(在 C 中),否则不能直接分配给 an int(在 C++ 中 >= 11)。这两种语言都允许将指针转换为整数,但它们不提供隐式执行的此类转换(尽管某些编译器将其作为扩展提供)。此外,虽然将空指针转换为整数以产生值 0 是很常见的,但标准并不保证这一点。如果你想要一个类型int和值为 0的常量,那么拼写它0

  • 我可能会因此而进入未定义行为吗?

是的,在任何NULL扩展为具有类型的值void *或任何其他不能直接分配给int. 该标准没有定义您对此类实现的赋值行为,因此其行为未定义。

  • 是否允许以这种方式使用 NULL 进行操作?

这是一种糟糕的风格,它会在某些系统和某些情况下崩溃。由于您似乎在使用 GCC,如果您使用该-Werror选项进行编译,它会在您自己的示例中中断。

  • 在算术表达式中使用 NULL 作为数值有什么问题吗?

是的。根本不保证有数值。如果你的意思是 0 那就写 0,这不仅定义明确,而且更短更清晰。

  • 在这种情况下,C++ 的结果如何?

C++ 语言在转换方面比 C 更严格,并且对 有不同的规则NULL,但在那里,实现也可能提供扩展。同样,如果你的意思是 0 那么这就是你应该写的。

  • 您应该指出,“更典型的类型为 `void *`”仅适用于 C。`void *` 不是 C++ 的合法类型(因为您不能将 `void*` 分配给任何其他指针类型) 。在 C++89 和 C++03 中,事实上 `NULL` *必须*是 `int` 类型,但在更高版本中它可以是(通常是)`nullptr_t`。 (4认同)

Sto*_*ica 32

NULL是一些空指针常量。在 C 中,它可能是一个带有 value 的整数常量表达式,也可能是一个转换0为 的表达式void*,后者更有可能。这意味着您不能假设NULL可以与零互换使用。例如,在此代码示例中

char const* foo = "bar"; 
foo + 0;
Run Code Online (Sandbox Code Playgroud)

替换0withNULL不能保证是一个有效的 C 程序,因为两个指针之间的加法(更不用说不同的指针类型)没有定义。由于违反约束,它将导致发出诊断。加法操作数将无效


至于 C++,情况有些不同。缺乏从void*其他对象类型到其他对象类型的隐式转换意味着它NULL在历史上被定义为0C++ 代码。在 C++03 中,你可能会侥幸逃脱。但是从 C++11 开始,它可以合法地定义为nullptr关键字。现在再次产生错误,因为std::nullptr_t可能不会添加到指针类型。

如果NULL定义为nullptr那么即使您的实验也无效。没有从std::nullptr_t到整数的转换。这就是为什么它被认为是更安全的空指针常量。

  • @jamesqf,任何值为 0 的整数常量都是空指针常量(在 C 中)与硬件指针实现无关。另请注意,标准 C 在任何情况下都不识别近指针和远指针之间的区别,但支持指针到指针的分配。它还支持(某些)指针比较,这为分段寻址格式(例如 286 的)带来了有趣的问题。 (3认同)
  • @jamesqf 根据 [C 常见问题解答](http://c-faq.com/null/machexamp.html),回复:将 `0` 设为空指针常量:“显然是对所有现有的写得不好的 C 语言的一个安慰做出错误假设的代码” (2认同)

eer*_*ika 22

我是否可以使用 NULL 指针代替 0 的值?

int i = NULL;
Run Code Online (Sandbox Code Playgroud)

规则因语言及其版本而异。在某些情况下,您可以,而在其他情况下,您不能。无论如何,你不应该。如果你很幸运,你的编译器会在你尝试时发出警告,或者甚至更好,编译失败。

在 C++ 中,在 C++11 之前(引自 C++03):

[lib.support.types]

NULL 是此国际标准中实现定义的 C++ 空指针常量。

将空指针常量用作整数几乎没有意义。然而...

[转化率]

空指针常量是整数类型的整数常量表达式 (5.19) 右值,其计算结果为零。

因此,即使它是荒谬的,它在技术上也会起作用。由于这种技术性,您可能会遇到滥用NULL.

自 C++11(引自最新草案):

[转化率]

空指针常数是一个整数文字([lex.icon])与零值或类型的std的prvalue?::?nullptr_叔

Astd?::?nullptr_­t不能转换为整数,因此NULL作为整数使用只能有条件地工作,具体取决于语言实现所做的选择。

PSnullptr是类型的纯右值std?::?nullptr_­t。除非您需要在 C++11 之前的版本中编译您的程序,否则您应该始终使用nullptr代替NULL.


C 有点不同(引自 C11 草案 N1548):

6.3.2.3 语言/转换/其他操作数/指针

3 值为 0 的整数常量表达式, 或转换为 type 的此类表达式void *,称为空指针常量。...

因此,这种情况类似于 C++11 之后的情况,即NULL根据语言实现所做的选择,有条件地滥用作品。


Dam*_*mon 10

是的,但根据实现,您可能需要演员表。但是,是的,它是 100% 合法的,否则。

虽然它真的,真的,真的很糟糕的风格(不用说?)。

NULL是,或者过去,实际上不是 C++,而是 C。然而,与许多 C 遗留系统一样,该标准确实有两个子句([diff.null] 和 [support.types.nullptr]),它们在技术上构成了NULLC++。它是一个实现定义的空指针常量。因此,即使它的风格很糟糕,从技术上讲,它也与 C++ 一样。
正如脚注中所指出的,可能的实现可能是00L,但不是 (void*)0

NULL当然可以(标准没有明确说明,但它几乎是0或之后剩下的唯一选择0L)是nullptr. 几乎从来没有这种情况,但这是一种合法的可能性。

编译器向您显示的警告表明编译器实际上不兼容(除非您在 C 模式下编译)。因为,好吧,根据警告,它确实转换了一个空指针(不是nullptr哪个是 of nullptr_t,哪个是不同的),所以显然 的定义NULL确实是(void*)0,它可能不是。

无论哪种方式,您都有两种可能的合法(即编译器未损坏)情况。要么(现实情况),NULL类似于0or 0L,那么您将“零或一”转换为整数,您就可以开始了。

或者NULL确实是nullptr。在这种情况下,你有一个大约有比较以及明确定义的转换担保鲜明的值整数,但偏偏不整数。然而,它确实有一个明确定义的到bool(导致false)的转换,并且bool有一个明确定义的到整数的转换(导致0)。

不幸的是,这是两次转换,所以它不在[conv] 中指出的“零或一”之内。因此,如果您的实现定义NULLnullptr,那么您将必须为您的代码添加显式转换才能正确。


phu*_*clv 6

来自 C 常见问题解答:

问:如果 NULL 和 0 等价于空指针常量,我应该使用哪个?

答:只有在指针上下文中NULL0是等价的。NULL应该不是需要另一种为0时,可以使用,即使它可能会奏效,因为这样做发出了错误的文体消息。(此外,ANSI 允许 NULL 的定义为((void *)0),这在非指针上下文中根本不起作用。)特别是,NULL在需要 ASCII 空字符 (NUL) 时不要使用。提供您自己的定义

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


pmg*_*pmg 5

免责声明:我不懂 C++。我的回答并不意味着适用于 C++ 的上下文

'\0'是一个int零值,100% 完全像0.

for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;
Run Code Online (Sandbox Code Playgroud)

在 pointers 的上下文中0NULL100% 等效:

if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;
Run Code Online (Sandbox Code Playgroud)

都是 100% 等价的。


注意事项 ptr + NULL

的情况下ptr + NULL不是该指针。C语言中没有对指针相加的定义;指针和整数可以相加(或相减)。在ptr + NULL如果任一ptr或者NULL是一个指针,其他必须是一个整数,所以ptr + NULL是有效(int)ptr + NULL还是ptr + (int)NULL和根据的定义ptrNULL一些行为可以预期:这一切工作,预警指针和整数,未能编译之间的转换,.. .

  • @JesperJuhl:**在指针的上下文中**它们是 100% 等价的。我不知道`nullptr`是什么,但是`((void*)0)`和`0`(或`'\0'`)在指针的上下文中是等效的......`if (ptr == ' \0' /* 或等效的 0, NULL */)` (3认同)
  • 在指针的上下文中,是的......我的回答中强调了条件,谢谢 (2认同)

Bui*_*ded 5

不,不再喜欢使用NULL(旧的指针初始化方式)。

从 C++11 开始:

关键字nullptr表示指针文字。它是 std::nullptr_t 类型的纯右值。存在从nullptr任何指针类型和任何指向成员类型的指针的空指针值的隐式转换。任何空指针常量都存在类似的转换,其中包括 typestd::nullptr_t和宏的值NULL

https://en.cppreference.com/w/cpp/language/nullptr

实际上,std::nullptr_t是空指针文字的类型,nullptr。它是一个独特的类型,它本身不是指针类型或指向成员类型的指针。

#include <cstddef>
#include <iostream>

void f(int* pi)
{
   std::cout << "Pointer to integer overload\n";
}

void f(double* pd)
{
   std::cout << "Pointer to double overload\n";
}

void f(std::nullptr_t nullp)
{
   std::cout << "null pointer overload\n";
}

int main()
{
    int* pi; double* pd;

    f(pi);
    f(pd);
    f(nullptr);  // would be ambiguous without void f(nullptr_t)
    // f(0);  // ambiguous call: all three functions are candidates
    // f(NULL); // ambiguous if NULL is an integral null pointer constant 
                // (as is the case in most implementations)
}
Run Code Online (Sandbox Code Playgroud)

输出:

Pointer to integer overload
Pointer to double overload
null pointer overload
Run Code Online (Sandbox Code Playgroud)