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


73

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

或这样做有什么问题吗?


例如,例如:

int i = NULL;

代替:

int i = 0;

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

#include <stdio.h>

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

    return 0;
}

输出:

0

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

warning: initialization makes integer from pointer without a cast [-Wint-conversion] 

但结果仍然相同。


  • 我是否会由此陷入“不确定行为”?
  • 可以这样使用NULL吗?
  • NULL在算术表达式中用作数字值有什么问题吗?
  • 在这种情况下,C ++的结果和行为是什么?

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


评论不作进一步讨论;此对话已转移至聊天
塞缪尔·柳

最好问两个独立的问题,一个是C的问题,另一个是C ++的问题。
康拉德·鲁道夫

Answers:


82

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

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

  • 我可能会由此陷入不确定行为吗?

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

  • 这样允许使用NULL进行操作吗?

它的样式很差,在某些系统上和某些情况下会损坏。由于您似乎正在使用GCC,因此如果使用该-Werror选项进行编译,它将在您自己的示例中中断。

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

是。完全不能保证有一个数值。如果您的意思是0,则写0,它不仅定义明确,而且更短,更清晰。

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

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


4
您应该指出,“更通常具有类型void *”仅对C成立。 void *这不是C ++的合法类型(因为您不能将其分配void*给任何其他指针类型)。在C ++ 89和C ++ 03中,实际上NULL 必须是type int,但是在更高版本中,它可以是(通常是)nullptr_t
马丁·邦纳

你也错了转换void*int未定义行为。它不是; 它是实现指定的行为。
马丁·邦纳

@MartinBonnersupportsMonica,在C指定将指针转换为整数的情况下,转换的结果确实是实现指定的,但这不是我要说的。它是对具有未定义行为的整数类型的左值(无需通过强制转换进行显式转换)的指针的分配。该语言未在此处定义自动转换。
John Bollinger

@MartinBonnersupportsMonica,我进行了编辑,以更全面地考虑C ++注意事项。无论如何,中心主题同样适用于两种语言:如果您想要一个整数0,则将其显式写为适当类型的整数常量。
John Bollinger

31

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

char const* foo = "bar"; 
foo + 0;

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


至于C ++,情况有所不同。缺乏void*对其他对象类型的隐式转换,这意味着它NULL在历史上已如0C ++代码中所定义。在C ++ 03中,您可能会摆脱它。但是从C ++ 11开始,可以合法地将其定义为nullptrkeyword。由于std::nullptr_t可能未添加到指针类型,因此现在再次产生错误。

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


为了完整起见,0L还是一个​​空指针常量,可以NULL在两种语言中使用。
eerorika

1
@jamesqf该标准说,值为0的整数常量是一个空指针常量。因此,0L是空指针常量。
eerorika

1
@eerorika:世界需要什么,而忽略了现实的标准:-)因为如果我正确地记住了80286程序集,甚至不能将far指针分配为单个操作,所以编译器编写者必须专门-保护它。
jamesqf

2
@jamesqf根据C常见问题解答,re:使0空指针常量为常数:“显然是对所有现存的编写错误的C代码的错误理解”
Andrew Henle

3
@jamesqf,任何值为0的整数常量都是空指针常量(在C中)与硬件指针实现无关。还要注意,标准C在任何情况下都不会识别近和远指针之间的区别,但是它支持指针到指针的分配。它还支持(一些)指针比较,这对分段寻址格式(例如286格式)提出了有趣的问题。
John Bollinger

21

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

int i = NULL;

规则在语言及其版本之间有所不同。在某些情况下可以,在其他情况下则不能。无论如何,您都不应该。如果幸运的话,当您尝试编译器时会警告您,甚至更好,编译失败。

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

[lib.support.types]

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

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

[conv.ptr]

空指针常量是整数类型的整数常量表达式(5.19)rvalue,其值为零。

因此,即使它是荒谬的,从技术上讲也可以正常工作。由于这种技术,您可能会遇到写得不好的滥用程序NULL

从C ++ 11开始(引用最新草案):

[conv.ptr]

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

A std​::​nullptr_­t不能转换为整数,因此NULL,根据语言实现的选择,将as用作整数只能有条件地起作用。

PS nullptr是类型的prvalue std​::​nullptr_­t。除非需要在C ++ 11之前的版本中编译程序,否则应始终使用nullptr而不是NULL


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

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

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

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


10

是的,但是根据实现的不同,您可能需要强制转换。但是,是的,否则它是100%合法的。

尽管这确实是非常糟糕的风格(不用说?)。

NULL是(或曾经是),实际上不是C ++,而是C。但是,就像许多C语言遗产一样,该标准的确有两个从句(技术上称为NULLC ++)。它是实现定义的空指针常量。因此,即使样式不好,它在技术上也尽可能地像C ++。
脚注所指出的,可能的实现方式可以是0或可以0L,但不是 (void*)0

NULL当然可以(标准没有明确说明,但这几乎是0或之后唯一剩下的选择0Lnullptr。几乎从来没有这样,但这是合法的可能性。

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

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

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

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


6

从C常见问题解答:

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

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

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


5

免责声明:我不懂C ++。我的答案并不打算在C ++的上下文中应用

'\0'是一个int值为零的,与完全一样为100%0

for (int k = 10; k > '\0'; k--) /* void */;
for (int k = 10; k > 0; k--) /* void */;

在指针的上下文中0并且NULL100%等效:

if (ptr) /* ... */;
if (ptr != NULL) /* ... */;
if (ptr != '\0') /* ... */;
if (ptr != 0) /* ... */;

都是100%等效的。


注意事项 ptr + NULL

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


我已经看到#define NULL (void *)0了。您确定NULL和纯0等于100%吗?
machine_1

2
在指针的上下文中,是的...我的回答中强调的条件,谢谢
pmg

@phuclv:我绝对不了解C ++。我的答案(括号之间的位置除外)是C
pmg

@phuclv ptr + NULLNULL在指针上下文中使用
pmg

3
@JesperJuhl:在指针的上下文中,它们是100%等效的。我不知道是什么nullptr,但是((void*)0)0(或'\0')在指针的上下文中是等效的……if (ptr == '\0' /* or equivalent 0, NULL */)
pmg

5

不,不再首选使用NULL(指针初始化的旧方法)。

从C ++ 11开始:

关键字nullptr表示指针文字。它是std :: nullptr_t类型的prvalue。存在从nullptr任何指针类型到成员类型的任何指针的从隐式转换为null的指针值。对于任何null指针常量(包括type值和std::nullptr_tmacro),都存在类似的转换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)
}

输出:

Pointer to integer overload
Pointer to double overload
null pointer overload

问题是关于为整数分配NULL到0。从这个意义上讲,使用nullptr而不是NULL不会发生任何变化。
ivan.ukr,

他用单词作为“空指针”。
Mannoj

顺便说一句,C ++在C ++ 11之后没有NULL概念。作者可能对使用constexpr或定义旧方法初始化感到困惑。cn.cppreference.com/w/cpp/language/default_initialization
Mannoj
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.