当 (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 与其第一个元素的第一个字节相同。
示例:
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) ofp
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 与其第一个元素的第一个字节相同。