Соответствует ли это поведение сравнения указателя gcc / clang last-one или нестандартно? - PullRequest
8 голосов
/ 19 апреля 2019

Несмотря на то, что стандарт 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, но gcc для идеальных выходных данных >> 2 1 для одной из строк и из того, что я может сказать, что код, сгенерированный clang, подойдет и для другого.

Мне хорошо известно, что тот факт, что p сравнивается равным x[1], не означает, что последнее выражение может использоваться для доступа к тому же объекту, что и *p (или к любому объекту вообще, в этом отношении) , но я ничего не знаю в Стандарте, который запрещал бы вычисление x+1 или сравнение между полученным указателем и p. Я также не знаю ничего, что могло бы сделать такое сравнение непригодным для использования p для доступа к объекту, адрес которого он содержит.

Имеется ли какое-либо правдоподобное прочтение какой-либо опубликованной или черновой версии стандарта C, в соответствии с которой приведенный выше код вызывает неопределенное поведение, или в соответствии с которой возвращаемые значения из test1 и test2 не потребуются для совпадения с окончательными значениями y[0] или x[0], соответственно, или оптимизаторы в clang и gcc предназначены для обработки диалекта, который не является опубликованной или черновой версией стандарта?

PS - из стандартной тяги N1570 6.5.9p6:

6 Два указателя сравниваются равными, если и только если оба указателя равны нулю, оба указателя на тот же объект (включая указатель на объект и подобъект в его начале) или функцию, оба являются указателями на один за последним элементом одного и того же объекта массива, или один является указателем один за концом одного объекта массива, а другой - указатель на начало другого объект массива, который происходит сразу же после первого объекта массива в адресе пространство.

Стандарт никоим образом не подразумевает, что x[] должен следовать y[], и наоборот, но, похоже, явно предусматривает возможность того, что указатель, указывающий сразу за x, может быть сопоставлен с y и наблюдается равным.

1 Ответ

0 голосов
/ 19 апреля 2019

Это действительно ошибка компилятора.В большинстве случаев, когда p == x+i имеет значение true для некоторого указателя p, массива x и целого числа i, это правда, что p обязательно указывает на элемент в x (или программавыполнение кода с неопределенным поведением, и в этом случае компилятору разрешается предполагать, что p указывает на элемент в x).Если бы p действительно указывал на элемент в x, было бы верно, что *p = 2; не может изменить y[0], и поэтому было бы правильно, чтобы компилятор генерировал код, который возвращает 1, который был недавно присвоен y[0].Подсказки предполагают, что компилятор попытался оптимизировать процесс, в котором он «узнает» о p на основе истинности сравнения.

Этот вывод завершается ошибкой, когда x+i указывает на единицу за концом массива x.В C 2018 6.5.9 6 стандарт говорит нам, что это сравнение может оценить как истинное, даже если p указывает на другой объект, не связанный с x (но это происходит сразу же после него в памяти).Вывод компилятора должен быть более ограниченным.Учитывая, что p+j == x+i, где x является массивом n элементов, тот факт, что оценка верна, только означает, что p+k указывает на элемент x для j - ik<<code>n + j - i.(В рассматриваемом случае i = 1, j = 0 и k = 0, что не соответствует 0−1 ≤ 0 <1 + 0−1.) </p>

(Примечаниечто критерий, представленный выше, подразумевает, для рассматриваемого случая, что p-1 указывает на элемент x, так как 0−1 ≤ −1 <0, но мы знаем, что <code>p указывает на y инедопустимо указывать на x. Но использование его для доступа к p[-1] имеет поведение, не определенное стандартом, означающее, что допускаются любые предположения, включая то, что p[-1] является элементом x. Таким образом, компилятор разрешениспользовать критерий.)

...