当 (void *) p == (void *) *p - 标准对此怎么说?

When (void *) p == (void *) *p - What does the Standard say about this?

示例:

int a[99];
int (*p)[99] = &a;

// this prints 1
printf("%d\n", (void *) p == (void *) *p);

一般来说,如果p是一个指向数组的指针,那么p*p的对象表示(即位模式)是相等的。

我只是迷路了,完全不确定这种行为的可移植性。

所以,我很好奇标准是否保证了这种行为。如果是这样,有人可以引用所有保证它的相关段落吗?

这个比较保证是1。

C standard 的相关部分是关于相等运算符和指针比较的第 6.5.9p6 节:

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.

请特别注意粗体部分。这意味着两件事:1)指向结构的指针和指向其第一个成员(适当转换)的指针将比较相等,以及 2)指向数组的指针和指向其第一个成员的指针(同样,适当转换)将比较等于。

在您的特定情况下,p 指向一个数组,*p 是数组本身,在表达式中使用 *p 会产生指向其第一个成员的指针。两者都转换为 void * 以赋予它们通用类型。所以这个比较总是计算为 1.

In general, if p is a pointer to an array, then both the object representations (i.e. the bit patterns) of p and *p are equal.

如果p是指向数组的指针,那么*p就是数组。数组的位表示是数组元素的位表示的串联(因为 C 2018 6.2.5 20 说数组由连续分配的对象组成)。数组中的位通常不等于指针中的位。

但是,当数组用于表达式而不是作为一元 & 的操作数或 sizeof 的操作数或作为用于初始化数组的字符串文字时,数组是自动转换为指向其第一个元素的指针。数组*p的第一个元素是(*p)[0],所以*p自动转换为&(*p)[0].

那么问题是(void *) p是否等于(void *) &(*p)[0].

C 2018 6.3.2.3 1 告诉我们任何指向对象类型的指针都可以转换为 void *。但是,它并没有告诉我们指针为void *时比较的结果是什么。它确实告诉我们,将 void * 转换回其原始类型会产生一个与原始类型比较相等的指针。

C 2018 6.5.9 6告诉我们“两个指针比较相等当且仅当......,两者都是指向同一个对象的指针(包括指向对象的指针和开头的子对象)......”(我省略了其他一些这里不关心的情况。)给定两个 void *,我们如何处理这个问题?看起来它的意图是让一个指针“指向一个对象”,即使它当前是 void * 的形式。然后 (void *) p 指向数组, (void *) &(*p)[0] 指向其开头的子对象,因此它们比较相等。

(char *) p == (char *) *p 的语义会更清晰,因为 C 2018 6.3.2.3 7 告诉我们转换为 char * 会产生一个指向对象第一个字节的指针,而一个对象的第一个字节array 与其第一个元素的第一个字节相同。