(void*) ptr == ptr 总是为真吗?

Is (void*) ptr == ptr always true?

我从 last question 中删除了这个问题,因为我认为这是一个相当个人化的问题。所以我在标准中找到了指针转换的段落,其中关于我的问题的是:

6.3.2.3

Pointers

1 A pointer to void may be converted to or from a pointer to any object type. A pointer toany object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

...

4 Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.

现在只声明

(originaltype*)((void*)ptr) == ptr

总是正确的,但是

呢?
(void*) ptr == ptr

没有明确说明这是真的还是假的。还是我误解了第 1 段?

C 2018 6.5.9 讨论==。第 2 段指定了约束,(void *) ptr == ptr 满足约束,因为其中一个选项是“一个操作数是指向对象类型的指针,另一个是指向 void 的限定或非限定版本的指针”。然后第 5 段说“……如果一个操作数是指向对象类型的指针,而另一个是指向 void 的限定或非限定版本的指针,则前者将转换为后者的类型。”

因此,在 (void *) ptr == ptr 中,右操作数被转换为 (void *),所以表达式等价于 (void *) ptr == (void *) ptr,我们可以预期它的计算结果为真。

严格来说,6.3.2.3 中关于指针转换的子句只告诉我们将(void *) ptr 转换回其原始类型的结果将比较等于ptr。它没有告诉我们关于 (void *) ptr 的值的任何其他信息,因此,仅考虑此子句, (void *) ptr 的两个不同实例可能会产生不同的结果,只要它们包含足够的信息产生的东西在转换回来时与原始 ptr 比较相等。

回到 6.5.9,第 6 段告诉我们:

Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.

现在,我们当然希望 (void *) ptr == (void *) ptr 至少在某些时候是正确的。这怎么可能? (void *) ptr 不是空指针(假设 ptr 不是),我们也不希望这种情况被一个数组的末尾和另一种情况的开始所覆盖。所以我们预计,当 (void *) ptr == (void *) ptr 的计算结果为真时,一定是因为它处于“指向同一对象的指针”的情况或“指向同一数组对象的最后一个元素的指针”的情况。这似乎是解释该标准的唯一合理方式。如果是这样,那么这种情况(无论哪种情况有时适用)必须始终适用,并且“当且仅当”告诉我们 (void *) ptr == (void *) ptr 始终为真。

*如果 ptr 是指向对象类型的指针,则 (void *) ptr == ptr 等同于 (void *) ptr == (void *) ptr。右边的ptr隐式转换为void *。 (如果它是指向 constvolatile 限定类型的指针,则这些限定符会在隐式转换中丢失。)

(void *) ptr 肯定等于它自己,除非我们开一些无聊的玩笑,比如 ptr 是一个扩展为具有副作用的表达式的宏,可以在不同的评估中改变它的值,或者是 indeterminately-valued表达式,其用法是undefined-behavior.

如果ptr是一个指向函数的指针,那么(void *) ptr == ptr需要诊断;但很明显,讨论是关于对象类型的。