Страницы

Поиск по вопросам

понедельник, 16 декабря 2019 г.

strict aliasing и char*

#cpp #c #strict_aliasing


Пытаюсь понять, в какой мере strict aliasing rule не касается char.

Во многих источниках делается акцент на том, что char может накладываться на любой
тип, но не наоборот. Что это значит?

Допустим:

int i;

int *pi = &i;
char *pc = (char*)&i;

// Первый случай.
*pc = 0;
*pi = 1;
// *pc != 0?

// Второй случай.
*pi = 0;
*pc = 1;
// *pi != 0?


Исключим из рассмотрения порядок байтов, требования выравнивания и прочие штуки.  

Верно ли я понимаю, что в связи с однонаправленностью описанного правила, один из
описанных случаев будет корректен, а другой - вызовет неопределенное поведение?

Или же правило описывает иную ситуацию, и оба приведенных случая не выльются в неопределенное
поведение?

Идентично ли поведение C и C++?

Я совершенно запутался с этим sa...
    


Ответы

Ответ 1



Во-первых что такое "strict aliasing" (в двух словах) - это следствие из ограниченности перечня типов выражений, для которых в стандарте определяется поведение при обращение к объекту определенного типа. Это следствие заключается в том, что у компилятора появляется возможность считать, что указатели (и ссылки) несовместных типов никогда не указывают на одно и то же место в памяти, то бишь что они не являются алиасами одного и того же объекта. Более формально этот перечень определен так: 8.2 Properties of expressions [expr.prop] 11 If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined: 63 (11.1) — the dynamic type of the object, (11.2) — a cv-qualified version of the dynamic type of the object, (11.3) — a type similar (as defined in 7.5) to the dynamic type of the object, (11.4) — a type that is the signed or unsigned type corresponding to the dynamic type of the object, (11.5) — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object, (11.6) — an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union), (11.7) — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object, (11.8) — a char, unsigned char, or std::byte type. Что же особенного в char? Дело в том, что модель памяти основана на адресации байт и все объекты в языке имеют байтовое представление. Последний пункт в перечне как раз разрешает получение доступа к байтовому представлению (object representation). Однако заметьте, что эти правила относятся непосредственно к доступу к объекту, то бишь левой части выражения *pc = 0;. А само присваивание работает потому что для тривиально-копируемых объектов разрешено копирование байт туда-сюда. Далее, приведенный в вопросе "второй случай" на самом деле ничем не отличается от первого. Обратный пример тут должен был бы выглядеть вот так: alignas(int) char bytes[sizeof(int)]{}; char * pc{static_cast(bytes)}; int * pi{reinterpret_cast(pc)}; *pi = 1; // неопределенное поведение! Но это еще не все, дело в том, что в имеется возможность размещать объекты в памяти (provide storage)! alignas(int) char bytes[sizeof(int)]{}; char * pc{static_cast(bytes)}; int * pi1{new(pc) int{}}; *pi1 = 1; // определенное поведение! int * pi2{reinterpret_cast(pc)}; *pi2 = 1; // (внезапно) по-прежнему неопределенное поведение, хотя pi2 == pi1!

Комментариев нет:

Отправить комментарий