На языках, которые изобрел Деннис Ритчи, назвал C и описал в обоих изданиях Язык программирования C (но расширен для включения 64-битных типов), единственная проблема с приведенным выше кодом состоит в том, что существуетнет никакой гарантии, что автоматические char[]
объекты будут выровнены так, чтобы к ним можно было получить доступ через 64-битный указатель.Это не будет проблемой на платформах x86 или x64, но будет проблемой на платформах, основанных на других архитектурах, таких как ARM Cortex-M0.
Диалекты, обработанные реализациями, такими как MSVC или icc, которые поддерживают духC, описанный Комитетом по стандартам, включая принцип «Не мешайте программисту делать то, что должно быть сделано», и приложите усилия для поддержки низкоуровневого программирования, признает, что конструкция вида *(T*)pointerOfTypeU
может получить доступобъект типа U. Хотя стандарт не предписывает такую поддержку, вероятно, потому что авторы ожидали, что реализации предпримут хотя бы некоторые усилия для распознавания ситуаций, когда указатель одного типа был сформирован из lvalue другого типа, исчитает, что такое признание может быть оставлено как вопрос качества реализации.Обратите внимание, что в Стандарте нет ничего, что требовало бы, чтобы данный компилятор:
struct foo {int x[10]; };
void test(struct foo *p, int i)
{
struct foo temp;
temp = *p;
temp.x[i] = 1;
*p = temp;
}
распознавал, что на объект типа struct foo
могут повлиять действия по формированию int*
с адресом foo.x+i
, разыменовывает его, затем записывает в результирующее lvalue типа int
.Вместо этого авторы стандарта полагаются на качественные реализации, чтобы приложить некоторые усилия для распознавания очевидных случаев, когда указатель или lvalue одного типа получен из указателя или lvalue другого.
Компилятор icc, учитывая:
int test1(int *p1, float *q1)
{
*p1 = 1;
*q1 = 2.0f;
return *p1;
}
int test2(int *p2, int *q2)
{
*p2 = 1;
*(float*)q2 = 2.0f;
return *p2;
}
int test3(int *p3, float volatile *q3)
{
*p3 = 1;
*q3 = 2.0f;
return *p3;
}
будет предполагать, что, поскольку p1
и p2
являются разными типами, и не имеют заметных отношений , они не будут псевдонимами, но признают это, поскольку p2
и q2
имеют одинаковый тип, они могут идентифицировать один и тот же объект.Он также признает, что lvalue *(float*)q2
вполне очевидно основано на q2
.Следовательно, он признает, что доступ к *(float*)q2
может быть доступом к *p2
.Кроме того, icc будет обрабатывать volatile
как индикацию «не предполагайте, что вы понимаете все, что здесь происходит», и, таким образом, допускает возможность того, что доступ через q3
может странным образом повлиять на другие объекты.
Некоторые компиляторы, такие как clang и gcc, если только через -O0
или -fno-strict-aliasing
не нужно вести себя так, как нужно для низкоуровневого программирования, интерпретировать стандарт как оправдание, чтобы игнорировать очевидные связи между l-значениями разных типов, за исключением случаев, когдаэто настолько сильно сломало бы языковые конструкции, что сделало бы их практически бесполезными.Хотя они случайно признают, что доступ к someUnion.array[i]
является доступом к someUnion
, они распознают *(someUnion.array+i)
аналогично, даже если определение из someUnion.array[i]
равно *(someUnion.array+i)
.Учитывая, что стандарт рассматривает поддержку почти всего, что связано с хранилищем смешанного типа, как проблему «качества реализации», все, что можно сказать в действительности, это то, что компиляторы, которые подходят для разных целей, поддерживают разные комбинации конструкций.