每个空指针常量都是空指针吗?

huangapple go评论60阅读模式
英文:

Is every null pointer constant a null pointer?

问题

从C17草案(6.3.2.3 ¶3)中:

值为0的整数常量表达式,或将此类表达式强制转换为类型 void * 的表达式,称为空指针常量67) 如果将空指针常量转换为指针类型,则生成的指针称为空指针,保证与指向任何对象或函数的指针不相等。

67)NULL<stddef.h>(以及其他头文件)中被定义为一个空指针常量[...]。

由此可见,以下内容是空指针常量00UL(void *)0(void *)0ULNULL

进一步可见,以下内容是空指针(int *)0(int *)0UL(int *)(void *)0(int *)(void *)0UL(int *)NULL。有趣的是,这些都不是"空指针常量";请参阅此处

以下空指针常量确实是空指针(因为void *是指针类型,而00UL是空指针常量):(void *)0(void *)0UL。在这方面,根据C17草案(6.2.5 ¶19-20):

void 类型包含一组空值;它是一个不完整的对象类型,不能完成。
[...]
指针类型 可以派生自函数类型或对象类型,称为引用类型。[...] 指针类型是一个完整的对象类型。

void 本身不是指针类型,而是不完整的对象类型。但 void * 是指针类型。

但似乎以下内容是空指针常量但不是空指针(因为没有强制转换为指针类型):00ULNULL。(确切地说,标准只要求 NULL 被定义为 "空指针常量",它也可以被定义为同时是空指针的空指针常量。但似乎标准不要求 NULL 被定义为同时是空指针。)

每个空指针常量都是空指针吗?NULL 真的 不是空指针吗?)

最后(有点开玩笑):如果某些空指针常量不是空指针,它们是否从技术上来说可以被称为一种 "非空指针"?(这种措辞在标准中有时会出现。)请注意,从语言学角度来看,我们有所谓的括号悖论;我们可以将其理解为 "[非空] 指针" 或 "非-[空指针]"。

英文:

From the C17 draft (6.3.2.3 ¶3):

> An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.<sup>67)</sup> If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
>
> <sup>67)</sup>The macro NULL is defined in &lt;stddef.h&gt; (and other headers) as a null pointer constant [...].

From this, it follows that the following are null pointer constants: 0, 0UL, (void *)0, (void *)0UL, NULL.

It further follows that the following are null pointers: (int *)0, (int *)0UL, (int *)(void *)0, (int *)(void *)0UL, (int *)NULL. Interestingly, none of these are "null pointer constants"; see here.

The following null pointer constants are null pointers (because void * is a pointer type and 0 and 0UL are null pointer constants): (void *)0, (void *)0UL. In this regard, according to the C17 draft (6.2.5 ¶19-20):

> The void type comprises an empty set of values; it is an incomplete object type that cannot be completed.
> [...]
> A pointer type may be derived from a function type or an object type, called the referenced type. [...] A pointer type is a complete object type.

void is not a pointer type itself, and it is an incomplete object type. But void * is a pointer type.

But it seems that the following are null pointer constants which are not null pointers (because there is no cast to a pointer type): 0, 0UL, NULL. (To be precise, while the standard only requires that NULL be defined as "a null pointer constant", it would be permissible to define it as a null pointer constant which is also a null pointer. But it seems that the standard doesn't require NULL to be defined in such a way that it is simultaneously a null pointer.)

Is every null pointer constant a null pointer? (Is NULL really not a null pointer?)

Finally (and somewhat tongue-in-cheek): In case certain null pointer constants are not null pointers, would they technically be a kind of "non-null pointer"? (This wording appears in some places in the standard.) Note that linguistically we have a so-called bracketing paradox; we can read this as "[non-null] pointer" or "non-[null pointer]".

答案1

得分: 24

"每个空指针常量都是空指针吗?"
"简而言之:不是。"
"正如您已经观察到的,值为0的整数常量表达式是空指针常量,尽管它们没有指针类型。您还引用了规范中对空指针的定义:“将空指针常量[]转换为指针类型”。这意味着具有以下一般形式的空指针常量..."
(void *)(&lt;integer constant expression with value 0&gt;)
"...符合“空指针”的定义。整数常量表达式本身就是空指针常量,所以强制转换使整个表达式成为空指针(除了是空指针常量)。"
"另一方面,采用值为0的整数常量表达式形式的空指针常量不满足“空指针”的定义,语言规范中也没有其他规定使它们成为空指针。示例:00x00UL1 + 2 + 3 - 6。"

"似乎标准并不要求将NULL定义为同时是空指针。"
"正确。"

"每个空指针常量都是空指针吗?"
"绝对不是(参见上文),但对于大多数目的来说,这并不重要。"

"(NULL真的不是空指针吗?)"
"这取决于您的C实现。语言规范允许任何一种答案。在实际情况中,它在您可能会遇到的大多数实现中都是一个空指针。"

"如果某些空指针常量不是空指针,它们是否在技术上属于某种“非空指针”?"
"不是的。不是空指针的空指针常量根本不是指针。它们是整数。"

英文:

> Is every null pointer constant a null pointer?

TL;DR: no.

As you have already observed, integer constant expressions with value 0 are null pointer constants, despite not having pointer type. You have also quoted the specification's definition of null pointer: "a null pointer constant [] converted to pointer type". That means that null pointer constants of this general form ...

(void *)(&lt;integer constant expression with value 0&gt;)

... satisfy the definition of "null pointer". The integer constant expression is a null pointer constant itself, so the cast makes the overall expression a null pointer (in addition to being a null pointer constant).

On the other hand, null pointer constants that take the form of integer constant expressions with value 0 do not satisfy the definition of "null pointer", and there is no other provision in the language spec that would make them null pointers. Examples: 0, 0x00UL, 1 + 2 + 3 - 6.

> it seems that the standard doesn't require NULL to be defined in such a way that it is simultaneously a null pointer.

Correct.

> Is every null pointer constant a null pointer?

Definitely not (see above), but for most purposes, it does not matter.

> (Is NULL really not a null pointer?)

It depends on your C implementation. The language spec allows either answer. In practice, it is a null pointer in most implementations you're likely to meet.

> In case certain null pointer constants are not null pointers, would they technically be a kind of "non-null pointer"?

No. Null pointer constants that are not null pointers are not pointers at all. They are integers.

答案2

得分: 10

Sure, here's the translated content:

每个空指针常量都是空指针吗?

不是的,原因就在你引用的文本中:

如果将空指针常量转换为指针类型,所产生的指针,称为空指针,保证与任何对象或函数的指针比较时都不相等。

空指针常量不会自动成为指针,就像任何整数常量不会自动成为指针一样。常量值必须转换为指针类型才能产生空指针。

所产生的空指针不一定要为零值。它只需是不可能是任何对象或函数地址的值。这个值可以0x00000000(在我熟悉的实现中是这样的),也可以是0xFFFFFFFF,或者是0xDEADBEEF,或者是其他值。

英文:

> Is every null pointer constant a null pointer?

No, and the reason why is in the text you quoted:

> If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

A null pointer constant is not automatically a pointer, just like any integer constant is not automatically a pointer. The constant value must be converted to a pointer type to produce a null pointer.

The resulting null pointer does not have to be zero-valued. It only has to be a value that cannot be the address of any object or function. That value may be 0x00000000 (and on the implementations I'm familiar with it is), or it may be 0xFFFFFFFF, or it may be 0xDEADBEEF, or it may be something else.

答案3

得分: 5

The null pointer constant may be a void * or some integer type.

测试您的机器:

#include <stdio.h>
#include <stdlib.h>

#define NULL_TEST(n) _Generic((n), \
  void *: "void *", \
  int: "int", \
  long: "long", \
  default: "something else" \
)

int main(void) {
  printf("%s\n", NULL_TEST(NULL));
  printf("%s\n", NULL_TEST((void*)0));
  printf("%s\n", NULL_TEST(0));
  printf("%s\n", NULL_TEST(0L));
}

在我的机器上,我得到了以下输出。您的输出可能会因第一行而有所不同。

void *
void *
int
long
英文:

The null pointer constant may be a void * or some integer type.

Test on your machine:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define NULL_TEST(n) _Generic((n), \
  void *: &quot;void *&quot;, \
  int: &quot;int&quot;, \
  long: &quot;long&quot;, \
  default: &quot;something else&quot; \
)

int main(void) {
  printf(&quot;%s\n&quot;, NULL_TEST(NULL));
  printf(&quot;%s\n&quot;, NULL_TEST((void*)0));
  printf(&quot;%s\n&quot;, NULL_TEST(0));
  printf(&quot;%s\n&quot;, NULL_TEST(0L));
}

On my machine, I had the below output. Your output may vary for the first line.

void *
void *
int
long

答案4

得分: 5

不过,事实上,没有任何空指针常量是空指针!这是因为常量和指针是不同种类的实体。

空指针常量是具有特定形式的常量表达式。表达式是一系列标记,空指针常量被定义为具有特定形式的标记序列。

空指针是一个值。在C语言中,每种类型都有其一组潜在值。对于每种指针类型,该集合中的一个或多个值都是空指针。C标准没有正式定义值的概念。正式语义需要这样做(正式定义指针的值变得相当复杂,这就是为什么C标准,一份没有数学内容的英文文件,没有尝试这样做的原因)。

一个表达式在上下文中计算为一个值(可能引发副作用)。所有类型为指针类型的空指针常量都会计算为一个空指针。某些空指针常量(例如01L - 'z' / 'z')具有整数类型,它们不会计算为一个空指针:它们计算为一个空整数(即值为0的整数,C标准不使用术语“空整数”,因为它不是需要具有特定名称的特殊值)。

C标准保证,如果 e 是具有整数类型且值为0的常量表达式,那么将此值转换为指针类型的任何表达式都会计算为一个空指针。请注意,对于任意表达式,不会为其提供此保证:即使将f定义为int f(void) { return 0; },表达式(void*) f()可能不是空指针。

C标准允许NULL具有整数类型或指针类型。如果它具有指针类型,则表达式NULL会计算为一个空指针。如果它具有整数类型,则不会。

英文:

No. In fact, no null pointer constant is a null pointer! This is because constants and pointers are different kinds of entities.

A null pointer constant is a constant expression which has a particular form. An expression is a sequence of tokens, and null pointer constants are defined as sequences of tokens that have a particular form.

A null pointer is a value. In C, each type has its set of potential values. For each pointer type, one or more value in that set is a null pointer. The C standard does not define the concept of value formally. A formal semantics would need to do this (and formally defining values of pointers gets rather complicated, which is why the C standard, an English document written without mathematics, doesn't try).

An expression evaluates to a value in a context (possibly causing side effects). All null pointer constants whose type is a pointer type evaluate to a null pointer. Some null pointer constants (e.g. 0, 1L - &#39;z&#39; / &#39;z&#39;) have an integer type, and those do not evaluate to a null pointer: they evaluate to a null integer (i.e. an integer with the value 0 — the C standard does not use the expression “null integer” because it isn't anything remarkable that would need a specific name).

The C standard guarantees that if e is a constant expression with an integer type and the value 0, then any expression that converts this value to a pointer type evaluates to a null pointer. Note that this guarantee is not given for arbitrary expressions: (void*) f() might not be a null pointer even if f is defined as int f(void) { return 0; }.

The C standard allows NULL to have either an integer type or a pointer type. If it has a pointer type, the expression NULL evaluates to a null pointer. If it has an integer type, it doesn't.

答案5

得分: 3

"Another fun one is that ''\0'' has type 'int' (6.4.4.3(10)) and “The numerical value of the octal integer so formed specifies the value of the desired character or wide character” ((5)), and the same holds of a hexadecimal escape. So both ''\0'' and ''\x0'' are null pointer constants as well. In addition, “floating operands that are the immediate operands of casts” (which must be casts of an arithmetic type to an integral type) are among the legal “integer constant expressions,” so '(int)0.0' is a null pointer constant. So could be 'enum' values, the results of 'sizeof' (although all standard types have a size of at least 1, some compilers have zero-size fields as an extension) and '_Alignof' (although the Standard says this can only return positive powers of 2 and an alignment of 0 is ignored), and the results of an operator whose operands are integral types, for example 'X^X' or '!1'.

Several modern compilers define 'NULL' to be a special keyword, such as '__null' in gcc, or 'nullptr' if cross-compiled on C++. This lets the compiler catch an error if a program uses 'NULL' where an integral constant or 'void*' might implicitly be converted to an expression that is not a pointer, such as a boolean."

英文:

Another fun one is that &#39;\0&#39; has type int (6.4.4.3(10)) and “The numerical value of the octal integer so formed specifies the value of the desired character or wide character” ((5)), and the same holds of a hexadecimal escape. So both &#39;\0&#39; and &#39;\x0&#39; are null pointer constants as well. In addition, “floating operands that are the immediate operands of casts” (which must be casts of an arithmetic type to an integral type) are among the legal “integer constant expressions,” so (int)0.0 is a null pointer constant. So could be enum values, the results of sizeof (although all standard types have a size of at least 1, some compilers have zero-size fields as an extension) and _Alignof (although the Standard says this can only return positive powers of 2 and an alignment of 0 is ignored), and the results of an operator whose operands are integral types, for example X^X or !1.

Several modern compilers define NULL to be a special keyword, such as __null in gcc, or nullptr if cross-compiled on C++. This lets the compiler catch an error if a program uses NULL where an integral constant or void* might implicitly be converted to an expression that is not a pointer, such as a boolean.

答案6

得分: 2

C语言的设计使得在大多数情况下,至少在最初针对的平台上,指针和整数可以基本互换使用。对于char *p; int i;,编译器在处理p=0;时,基本上会以与处理i=0;相同的方式处理,只是前者会将值0写入p的地址,而后者会将值0存储到i的地址。编译器无需理解空指针的概念,因为用于将i设置为数值零的相同编译器逻辑同样可以有效地将p设置为一个不与任何对象关联且行为类似于值零的值。

C标准的编写方式不允许表达式的类型根据使用的上下文而变化。虽然在p=0;的赋值运算符的右操作数中,右操作数将具有指针类型,在i=0;中的右操作数将具有整数类型可能是有道理的,但标准的设计要求它们具有相同的类型。因为没有一种“正常”的类型可以在两个上下文中都使用,C标准的作者创建了一个特殊的“类型”,用于表示在两个上下文中都能够平等使用的表达式。我认为术语“空指针”常量比必要的要更令人困惑,而“通用零”可能更清晰,因为零代表的不仅仅是数字零,或者空指针,或者全零位模式,而更一般地代表了静态持续时间对象的默认值。

英文:

C was designed in such a way that, at least on the platforms for which it was originally targeted, pointers and integers could be treated essentially interchangeably in most contexts. Given char *p; int i;, a compiler processing p=0; would process it essentially the same way as i=0;, except that the former would write the value 0 to the address of p, while the latter would store the value 0 to the address of i. There was no need for a compiler to understand the concept of a null pointer, because the same compiler logic that would be used to set i to the numerical value zero could just as effectively set p to a value that wouldn't be associated with any object and would behave as the value zero.

The way the C Standard is written does not allow the type of an expression to vary depending upon the context where it is used. While it might make sense to say that the right-hand operand of the assignment operator in p=0; would have pointer type, and that the right-hand operator in i=0; would have integer type, the design of the Standard requires that they both have the same type. Because there is no "normal" type that could be used in both contexts, the authors of the C Standard created a special "type" for expressions which should be equally usable in both contexts. I think the term "null pointer" constant is more confusing than necessary, and that "universal zero" would be clearer, since what the zero represents isn't just the number zero, or a null pointer, or an all-zeroes bit pattern, but more generally the default value of a static-duration object.

huangapple
  • 本文由 发表于 2023年5月10日 22:14:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76219480.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定