我很好奇C18标准中的一句话:
两个指针比较相等当且仅当它们都是空指针,都是指向同一个对象的指针(包括指向一个对象的指针和它开头的子对象)或函数,都是指向同一数组最后一个元素之后的指针对象,或者一个是指向一个数组对象末尾的指针,另一个是指向另一个数组对象开头的指针,该对象恰好紧跟地址空间中的第一个数组对象。§ 6.5.9 6
为什么数组后面的对象必须是另一个数组?不能简单地成为与数组基类型相同类型的对象(例如int
紧跟在 之后的int[]
)?
难怪我试过这个代码:
#include <stdio.h>
struct test { int arr[10]; int i; };
int main() {
struct test t;
int *p, *q;
p = t.arr + 10;
q = &t.i;
if (p == q)
printf("Equal pointers.");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它产生相等的指针。这种行为是否根本没有保证,只是实现定义的巧合?
chu*_*ica 24
OP:为什么数组后面的对象必须是另一个数组?
它不是。“...开始不同的数组...”是一种简化。下一个规范是:
对于这些运算符而言,指向不是数组元素的对象的指针的行为与指向长度为 1 的数组的第一个元素的指针的行为相同,该数组的元素类型为对象的类型。C17dr § 6.5.9 7
OP:不能简单地是一个类型与数组基本类型相同的对象(比如
int
紧跟在 之后的int[]
)?
是的。
首先,在此处指定数组并不排除/禁止单个对象。内存中的单个对象与大小为 1 的数组无法区分。
(编辑:阅读此答案以获取标准中的引文,该标准在提及指针时明确说明了这一点)
其次,该标准还试图澄清您引用的声明,以下脚注指出了该规则适用的场景:
两个对象可以在内存中相邻,因为它们是更大数组的相邻元素或结构的相邻成员,它们之间没有填充,或者因为实现选择这样放置它们,即使它们不相关。
综上所述,标准在这里试图说明的是,一般而言,指向不同对象的两个指针不应该比较相等。然而,由于在内存中指向一个数组对象之外的对象是合法的,如果在那个位置碰巧有一个不同的(数组)对象,这样的指针与指向相邻对象的指针进行比较仍然是合法的。现在,由于对齐选择和填充,此位置可能有也可能没有有效对象,但如果有,则这些指针比较相等是可以接受的。
在您的示例中,如果我将数组更改为字符,则指针可能比较不相等,因为编译器会选择将 int 对齐到 4 个字节(在大多数 32 或 64 位平台上),从而引入填充。根据标准,这种行为仍然是合法的。
#include <stdio.h>
struct test { char arr[10]; int i; };
int main() {
struct test t;
int *p, *q;
p = (int*)(t.arr + 10);
q = &t.i;
if(p == q)
printf("Equal pointers.");
else
printf("Unequal pointers.");
return 0;
}
Run Code Online (Sandbox Code Playgroud)