Псевдоним указателя массива - неопределенное поведение? - PullRequest
14 голосов
/ 15 августа 2011

Вызывает ли следующий код неопределенное поведение (из-за нарушения псевдонимов или иным образом)?

int foo(int (*a)[10], int (*b)[5])
{
    (*a)[5]++;
    return (*b)[0];
}

int x[10];
foo(&x, (int (*)[5])&x[5]);

Обратите внимание, что соответствующий код, использующий простые int *, а не типы указатель на массив, был бы совершенно допустимымпотому что a и b будут указателями на один и тот же тип и, таким образом, позволят псевдонимы друг другу.

Edit: Интересное следствие, если это на самом деле нарушение псевдонимовЭто то, что это кажется хакерским, но верным способом получить семантику restrict до C99.Как в:

void some_func(int *aa, int *bb)
{
    int (*a)[1] = (void *)aa;
    int (*b)[2] = (void *)bb;
    /* Now **a and **b can be assumed by the compiler not to alias */
}

Предположительно, если вам нужно было получить доступ к фактическому массиву по каждому адресу, вы можете использовать SIZE_MAX-1 и SIZE_MAX-2 и т. Д. В качестве различных размеров.

1 Ответ

5 голосов
/ 15 августа 2011

Здесь вы не обращаетесь к объектам через указатели другого типа: вы не манипулируете объектами массива, на которые указывают a и b, а объектами, на которые указывают (*a)+5 и (*b)+0, а именно *((*a)+5) и *((*b)+0).Так как это указатели на один и тот же тип, они могут быть псевдонимами одного и того же объекта.

Неявное присваивание оператором ++ является допустимым присваиванием объекту, на который указывает (*b)+0: ++ isэквивалентно x = x + 1 (кроме того, что x оценивается только один раз) и для простого присвоения = стандарт гласит:

Если значение, хранящееся в объекте, считывается из другого объекта, который перекрывается вв любом случае хранилище первого объекта, затем перекрытие должно быть точным, и два объекта должны иметь квалифицированные или неквалифицированные версии совместимого типа;в противном случае поведение не определено.

Типы здесь точно такие же, а совпадение точное.

...