尽管 C 标准明确认识到这样一种可能性,即指向“刚刚过去”一个对象的地址可能会偶然与指向“指向”另一个不相关对象的地址进行比较,但 gcc 和 clang 似乎都在假设没有指针的情况下进行操作观察到的指向刚刚经过一个对象的对象可能指向另一个对象,如以下示例所示:
#include <stdio.h>
int x[1],y[1];
int test1(int *p)
{
y[0] = 1;
if (p==x+1)
*p = 2; // Note that assignment is to *p and not to x[1] !!!
return y[0];
}
int test2(int *p)
{
x[0] = 1;
if (p==y+1)
*p = 2; // Note that assignment is to *p and not to y[1] !!!
return x[0];
}
int (*volatile test1a)(int *p) = test1;
int (*volatile test2a)(int *p) = test2;
int main(void) {
int q;
printf("%llX\n",(unsigned long long)y - (unsigned long long)x);
q = test1a(y);
printf(">> %d %d\n", y[0], q);
q = test2a(x);
printf(">> %d %d\n", x[0], q);
return 0;
}
通过我对标准的阅读,该程序在标记行上的有效输出>>
会是>> 1 1
or >> 2 2
,但是 ideone 上的 gcc 输出>> 2 1
对于其中一行,据我所知,由 clang 生成的代码也会对另一行执行同样的操作。
我很清楚事实是p
比较等于x[1]
并不意味着后一个表达式可用于访问与以下内容相同的对象*p
(或任何对象,就此而言),但我不知道标准中没有任何内容禁止计算x+1
也不比较结果指针和p
。我也不知道有什么会导致这种比较p
无法用于访问其保存地址的对象。
对 C 标准的任何已发布版本或草稿版本是否有任何合理的解读,在该版本中,上述代码会调用未定义的行为,或者从test1
and test2
不需要匹配最终值y[0]
or x[0]
分别,或者 clang 和 gcc 中的优化器是否设计用于处理不是标准的已发布版本或草稿版本的方言?
PS--来自标准草案 N1570 6.5.9p6:
6 两个指针比较相等当且仅当两者都是空指针,并且都是指向
相同的对象(包括指向对象的指针和其开头的子对象)或函数,
两者都是指向同一数组对象最后一个元素之后的指针,或者一个是指针
一个指向一个数组对象的末尾,另一个是指向另一个数组对象的开头的指针
恰好紧随地址中第一个数组对象的数组对象
空间。
该标准并不以任何方式暗示x[]
必须遵循y[]
反之亦然,但似乎明确提供了指向刚刚过去的指针的可能性x
可能会比作y
并且观察到是相等的。