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

时间:2019-12-21 15:08:54

标签: c++ c pointers null language-lawyer

我可以使用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
  • 在数学表达式中使用NULL作为数字值有什么问题吗?
  • 在这种情况下,C ++的结果和行为是什么?

我已经阅读了What is the difference between NULL, '\0' and 0的答案,关于\00NULL之间的区别是什么,但是如果没有从中得到简洁的信息,完全可以使用{{1}}作为赋值和其他算术运算的值。

8 个答案:

答案 0 :(得分:62)

  

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

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

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

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

  
      
  • 是否可以通过这种方式使用NULL?
  •   

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

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

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

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

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

答案 1 :(得分:21)

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

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

0代替NULL不能保证是有效的C程序,因为未定义两个指针之间的加法(更不用说不同指针类型了)。由于违反约束,将导致发出诊断信息。 The operands for addition will not be valid


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

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

答案 2 :(得分:13)

  

我可以使用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]

     

空指针常量是值为零或std :: nullptr_­t 类型的prvalue的整数文字([lex.icon])。

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

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


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

  

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

     

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

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

答案 3 :(得分:5)

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

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

NULL实际上是(或者曾经是)不是C ++,而是C。然而,像许多C传统一样,标准 does 有两个子句( [diff.null]和[support.types.nullptr])从技术上讲是NULL C ++。这是实现定义的空指针常量。因此,即使样式不好,它在技术上也尽可能地像C ++。
正如footnote中指出的那样,可能的实现方式可以是00L,但不是 (void*)0

NULL当然可以(标准没有明确说明,但是在00L之后,它几乎是唯一的选择)是nullptr。几乎从来没有这样,但这是合法的可能性。

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

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

或者NULL的确是nullptr。在这种情况下,您拥有一个独特的值,该值可以保证比较以及从整数的明确定义的转换,但不幸的是,不能从整数。但是,它确实有明确定义的对bool的转换(导致false),并且bool有明确定义的对整数的转换(导致0

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

答案 4 :(得分:3)

来自C常见问题解答:

  

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

     

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

     

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

答案 5 :(得分:2)

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

'\0'是值为{0的int,与0一样,仅为100%。

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

在指针的上下文中0NULL等于100%:

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

都是100%等效的。


关于ptr + NULL

的注释

ptr + NULL的上下文是不是指针的上下文。没有定义使用C语言添加指针。可以将指针和整数相加(或相减)。在ptr + NULL中,如果ptrNULL是指针,则另一个必须是整数,因此ptr + NULL实际上是(int)ptr + NULLptr + (int)NULL,并根据ptrNULL的定义,可以预期以下几种行为:都可以正常工作,警告指针和整数之间的转换,编译失败,...

答案 6 :(得分:1)

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

从C ++ 11开始:

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

输出:

Pointer to integer overload
Pointer to double overload
null pointer overload

答案 7 :(得分:-2)

是,

#include
int main()
{
int *ptr = NULL;
printf("The value of ptr is %u",ptr);
return 0
'enter code here`