这个gcc / clang过去一指针比较行为是符合标准还是非标准?

时间:2019-04-18 22:12:31

标签: c gcc optimization clang

即使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>> 2 2,但是ideone输出>> 2 1上的gcc为其中一行,从我可以看出clang生成的代码对另一行也是如此。

我很清楚p等于x[1]的事实并不意味着后一个表达式可用于访问与*p相同的对象(或位于全部,就此而言),但我不知道标准中的任何内容都禁止计算x+1以及结果指针与p之间的比较。我还知道没有任何东西会使这种比较使p无法用于访问其地址所在的对象。

是否存在对上述标准调用未定义行为或不需要从test1test2返回的值的C标准的任何已发布或草稿版本的合理阅读?分别与y[0]x[0]的最终值匹配,还是clang和gcc中的优化器设计用于处理不是标准的已发布或草稿版本的方言?

PS-根据标准草案N1570 6.5.9p6:

  

6当且仅当两个都是空指针,并且两个都是指向指针的指针时,两个指针比较相等       相同的对象(包括指向对象和子对象开头的指针)或函数,       两者都是指向同一数组对象最后一个元素之后的指针,或者一个为指针       指向一个数组对象的末尾,另一个是指向另一个数组的开始的指针       恰好紧跟地址中第一个数组对象的数组对象       空间。

该标准并不以任何方式暗示x[]必须遵循y[],反之亦然,但这似乎是在明确规定指针指向x的指针可能会与y进行比较并观察为相等。

2 个答案:

答案 0 :(得分:1)

不,这不是编译器错误。您的程序有未定义的行为。

在函数 test1 中,您将 p 与 x+1 进行比较,但 p==y 因此指针完全不相关,这会给您带来不确定的行为。编译器不需要为此发出警告。在这种情况下是不可能检测到的。

因此,两个函数中与 p 的比较结果可能为真,也可能不为真,但无法预测会发生什么,具体取决于内存布局。

编译器错误很少见,如果您认为自己发现了错误,请三思并寻找未定义的行为。

答案 1 :(得分:0)

这确实是一个编译器错误。在大多数情况下,当p == x+i的某个指针p,数组x和整数i的值为true时,p必然指向一个x中的元素(或程序正在执行具有未定义行为的代码,在这种情况下,允许编译器假定p指向x中的元素)。如果p确实指向x中的元素,那么*p = 2;不能更改y[0]是正确的,因此编译器生成返回以下内容的代码是正确的最近分配给y[0]的1。这些线索表明,编译器已尝试进行优化,以通过比较的事实“了解” p

x+i指向数组x的末尾时,此推论失败。在C 2018 6.5.9 6中,该标准告诉我们,即使p指向与x无关的另一个对象,该比较也可能为true(但是恰好在内存中紧随其后) 。编译器的推论应该更加有限。给定p+j == x+i,其中xn个元素的数组,则评估为真的事实仅意味着p+k指向{ {1}}-xj <i + k-n。 (在问题中,j = 1,i = 0和i = 0,这使0-1≤0 <1 + 0-1失败。)

(请注意,对于问题的情况,上述准则暗示j指向k的元素,因为0−1≤-1 <0成立,但我们知道p-1指向x,对指向p无效,但是使用它来访问y的行为未由标准定义,这意味着可以进行任何假设,包括xp[-1]的元素。因此允许编译器使用该准则。)