При написании Стандарта lvalue структуры или типа объединения может использоваться для доступа к объекту типа члена, но нет положения, позволяющего произвольное lvalue структуры или объединения тип члена для доступа к объекту типа struct или union. Поскольку, конечно, было бы абсурдно утверждать, что код не может использовать член структуры или объединения lvalue (который, конечно, будет иметь тип этого члена) для доступа к структуре или объединению, все компиляторы поддерживают некоторые общие шаблоны доступа. Поскольку компиляторы допускают такой доступ при различных обстоятельствах, однако, стандарт рассматривает всю поддержку таких доступов как проблему качества реализации, а не пытается точно указать, когда такая поддержка требуется.
Подход, наиболее соответствующий стандарту формулировка, которая позволила бы наиболее полезные оптимизации, и в то же время поддерживала бы большую часть кода, который должен был бы выполнять перетаскивание типов или другие методы, означала бы, что для целей N1570 6.5p7 указатель, который визуально получен из указателя или lvalue данного типа может использоваться в контексте такого деривации для доступа к вещам, которые (для целей 6.5p7) будут доступны с использованием lvalue этого типа. При таком подходе данный фрагмент кода, такой как:
struct foo { int index,len; int *dat; };
void test1(struct foo *p)
{
int *pp = &foo->len;
*pp = 4;
}
void test2(struct foo *p, int dat)
{
if (p->index < p->len)
{
p->dat[p->index] = dat;
p->index++;
}
}
должен признать, что в test1
доступ к *pp
может получить доступ к struct foo
объекту *p
, поскольку pp
формируется из p
С другой стороны, компилятору не требуется учитывать в test2
возможность того, что объект типа struct foo
или его члены, такие как p->index
, могут быть изменены с помощью указателя p->dat
, потому что ничего внутри test2
приведет к тому, что адрес struct foo
или любой его части будет сохранен в p->dat
.
Clang и g cc, однако вместо этого выберите другой подход, ведущий себя как будто 6.5 p7 позволяет получить доступ к членам структуры через произвольные указатели их типов, но к элементам объединения вообще нельзя получить доступ через указатели, исключая арифметику указателей c, подразумеваемую выражениями в скобках. Учитывая, что union { uint16_t h[4]; uint32_t w[2];} u;
clang и g cc распознают, что доступ к u.h[i]
может взаимодействовать с u.w[j]
, но не распознают, что *(u.h+i)
может взаимодействовать с *(u.w+j)
, даже если стандарт определяет значение первого выражения со скобками как эквивалентные последним формам.
Учитывая, что компиляторы последовательно обрабатывают все эти конструкции с пользой, когда псевдонимы на основе типов отключены. Стандарт, однако, не предъявляет никаких требований даже во многих распространенных случаях, и clang и g cc не дают никаких обещаний относительно поведения конструкций, не предписанных Стандартом, даже если все версии на сегодняшний день обрабатывали такие конструкции с пользой. Таким образом, я бы не рекомендовал полагаться на clang или g cc, чтобы с пользой обрабатывать все, что связано с доступом к хранилищу как к разным типам в разное время, кроме случаев использования -fno-strict-aliasing
, и их использование не является проблемой при использовании этой опции, поэтому я рекомендую просто использовать эту опцию, если только или пока clang и g cc не примут более определенную абстракцию.