(int *)0是否为空指针?

时间:2014-01-27 17:04:28

标签: c language-lawyer null-pointer

这可以被认为是this question的扩展(我只对C感兴趣,但是添加C ++来完成扩展)

6.3.2.3.3的C11标准说:

  

值为0的整型常量表达式,或者类型为void *的表达式,称为空指针常量。

我个人对此的看法是0(void *)0表示空指针,其整数值实际上可能不是0,但不包括0强制转换为任何其他类型。< / p>

但是,标准然后继续:

  

如果将空指针常量转换为指针类型,则生成的指针称为空指针,...

(int *)0作为空指针,因为强制转换是显式转换(C11,6.3),它在转换方法下列出。

然而,令我惊讶的是以下短语

  

...或此类表达式转换为void * ...

有了上述语义,这句话​​似乎完全没用。问题是,这句话完全没用吗?如果没有,它有什么影响?因此,是否(int *)0空指针?


可以帮助讨论的另一个问题如下。 (long long)123被视为“123转换为long long”或“123类型为long long”。换句话说,(long long)123中是否有任何转化?如果没有,那么上面的第二个引号不会覆盖(int *)0作为空指针。

2 个答案:

答案 0 :(得分:29)

简短回答:

在C和C ++中,(int *)0是一个常量表达式,其值为空指针。但是,它不是空指针常量。我知道,constant-expression-which-value-is-null-pointer和null-pointer-constant之间唯一可观察到的区别是null-pointer-constant可以赋值给任何一个lvalue。指针类型,但是constant-expression-which-value-is-a-null-pointer具有特定的指针类型,并且只能分配给具有兼容类型的左值。在C中,而不是C ++,(void *)0也是一个空指针常量;这是void *的特殊情况,与一般的C-but-not-C ++规则一致,void *与任何其他指针到对象类型的赋值兼容。

例如:

long *a = 0;           // ok, 0 is a null pointer constant
long *b = (long *)0;   // ok, (long *)0 is a null pointer with appropriate type
long *c = (void *)0;   // ok in C, invalid conversion in C++
long *d = (int *)0;    // invalid conversion in both C and C++

在这种情况下,空指针常量(void *)0与类型为void *的constant-expression-which-is-a-null指针之间的差异是可见的,即使在C:

typedef void (*fp)(void);  // any pointer-to-function type will show this effect

fp a = 0;                  // ok, null pointer constant
fp b = (void *)0;          // ok in C, invalid conversion in C++
fp c = (void *)(void *)0;  // invalid conversion in both C and C++

此外,它现在没什么问题,但是自从你提出来之后:无论 long *的空指针的位代表是什么,所有的这些断言的行为如评论所示:

// 'x' is initialized to a null pointer
long *x = 0;

// 'y' is initialized to all-bits-zero, which may or may not be the
// representation of a null pointer; moreover, it might be a "trap
// representation", UB even to access
long *y;
memset(&y, 0, sizeof y);

assert (x == 0);         // must succeed
assert (x == (long *)0); // must succeed
assert (x == (void *)0); // must succeed in C, unspecified behavior in C++
assert (x == (int *)0);  // invalid comparison in both C and C++

assert (memcmp(&x, &y, sizeof y) == 0); // unspecified

assert (y == 0);         // UNDEFINED BEHAVIOR: y may be a trap representation
assert (y == x);         // UNDEFINED BEHAVIOR: y may be a trap representation

&#34;未指定&#34;比较不会引起不确定的行为,但标准并没有说明它们是评估是真还是假,并且实现不需要记录它们中的哪一个,或者甚至选择一个并坚持它。如果您多次调用它,则上述memcmp在返回0和1之间交替完全有效。


标准引语的长答案:

要了解空指针常量是什么,首先必须了解整数常量表达式是什么,并且它非常多毛 - 一个完整的理解要求您详细阅读C99的6.5和6.6节。这是我的总结:

  • 常量表达式是任何C表达式,编译器可以在不知道任何对象(const或其他值)的情况下将其计算为常量;但是,{{1} }值是公平的游戏),没有副作用。 (这是大约25页标准的大幅简化,可能不准确。)

  • 整数常量表达式是常量表达式的受限子集,可方便地在单个段落中定义,C99 6.6p6及其脚注:

      

    整数常量表达式 96 应具有整数类型,并且只能具有整数常量的操作数,枚举常量,字符常量,结果为整数常量的enum表达式,以及浮点常量这是演员的直接操作数。整数常量表达式中的转换运算符只能将算术类型转换为整数类型,除非作为sizeof的操作数的一部分   操作

         

    96 整数常量表达式用于指定结构的位字段成员的大小,枚举常量的值,数组的大小或大小写的值不变。适用于[sizeof]中使用的整型常量表达式的其他约束将在6.10.1中讨论。

    为了讨论的目的,重要的是

      

    Cast运算符...只能将算术类型转换为整数类型

    这意味着#if 不是 整数常量表达式,尽管它是一个常量表达式。

C ++ 98定义似乎或多或少等价,模C ++特性和与C的偏差。例如,C ++中字符和布尔类型与整数类型的强分离意味着C ++标准说的是&# 34; 积分常量表达式&#34;而不是&#34; 整数常量表达式&#34;,然后有时不仅需要一个整数常量表达式,而且需要整数类型的整数常量表达式,不包括{{ 1}},(int *)0char(也许还有wchar_tbool?我从文中不清楚这一点。)

现在,空指针常量的C99定义就是这个问题的全部内容,所以我将重复它:6.3.2.3p3说

  

一个整数常量表达式,其值为0,或者这样的表达式强制转换为类型   signed char被称为空指针常量。如果将空指针常量转换为a   指针类型,结果指针,称为空指针,保证比较不等于指向任何对象或函数的指针。

Standardese非常非常直接。这两句话的意思完全相同:

  

值为0的整型常量表达式称为空指针常量   值为0的整数常量表达式(强制转换为unsigned char类型) 空指针常量
  当任何空指针常量转换为指针类型时,结果指针称为空指针,并保证比较不等...

(斜体 - 术语的定义。粗体 - 我的重点。)那么这意味着,在C中,void *void *是两种完全相同的写法,即空指针类型(long *)0

C ++与众不同。等效文本是C ++ 98 4.10 [conv.ptr]:

  

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

这就是全部。 &#34;整数类型的整数常量表达式r值&#34;与C99&#34;&#34;整数常量表达式&#34;非常相似,但有一些东西在C中有资格而不是C ++:例如,在C中字符文字(long *)(void *)0是一个整型常量表达式,因此是一个空指针常量,但在C ++中,不是整数类型的整数常量表达式,因此它也不是空指针常量。

更重要的是,C ++没有&#34;或者这样的表达式转换为long *&#34;条款。这意味着'\x00' 不是 C ++中的空指针常量。它仍然是空指针,但它不与任何其他指针类型分配兼容。这与C ++的通常选择型系统一致。

C ++ 11(但不是,AFAIK,C11)修改了&#34;空指针&#34;的概念,为它们添加了一种特殊类型(void *)和一个评估为a的新关键字空指针常量(((void *)0))。我不完全理解这些更改,我不会尝试解释它们,但我很确定裸nullptr_t仍然是C ++ 11中的有效空指针常量。

答案 1 :(得分:9)

评估表达式(int*)0会产生类型为int*的空指针。

(int*)0不是空指针常量

空指针常量是一种特殊的表达式,可能出现在C源代码中。 空指针是可能在正在运行的程序中出现的值。

C和C ++(两种不同的语言)在这方面的规则略有不同。 C ++没有“或者这样的表达式强制转换为void*”字样。但我认为这不会影响你问题的答案。

至于您关于(long long)123的问题,我不确定它是如何相关的,但表达式123的类型为int,而强制转换指定了{{1}的转换转到int

我认为核心混淆是假设long long中的强制转换没有指定转换,因为(int*)0已经是空指针常量。但是空指针常量不一定是指针类型的表达式。特别是,表达式0既是空指针常量,也是类型0的表达式;它不是任何指针类型。术语空指针常量需要被视为一个单一概念,而不是一个短语,其含义取决于构成它的单个单词。