Стандарт позволяет нам приводить указатели на типы объектов друг к другу, если они соответствующим образом выровнены. 6.3.2.3(p7)
:
Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. Если результирующий указатель неправильно выровнен68) для указанного типа, поведение не определено.
Стандарт позволяет нам копировать представление объекта в char[sizeof(the_object_type)]
6.2.6.1(p4)
:
Значение может быть скопировано в объект типа unsigned char [n] (например, с помощью memcpy); результирующий набор байтов называется объектным представлением значения.
Кроме того, в Стандарте прямо указано, что
Два значения (кроме NaN) с одним и тем же представлением объекта сравниваются как равные, но значения, которые сравниваются как равные, могут иметь разные представления объекта.
Рассмотрим следующий код:
struct contains_64_t{
uint64_t value;
};
int main(int args, const char *argv[]){
_Alignas(struct contains_64_t)
char buf_2_64t[2 * sizeof(struct contains_64_t)];
struct contains_64_t c64_1;
c64_1.value = 1;
struct contains_64_t c64_2;
c64_2.value = 2;
memcpy(buf_2_64t, &c64_1, sizeof(c64_1));
memcpy(buf_2_64t + sizeof(c64_1), &c64_2, sizeof(c64_2));
//suitably aligned, ok
struct contains_64_t *c64_ptr = (struct contains_64_t*) buf_2_64t;
printf("Value %"PRIu64"\n", c64_ptr -> value);
}
ВОПРОС. Педантично ли писать такой код? Если нет, то с какой проблемой мы можем столкнуться, если сделаем это?
Из того, что я вижу,
мы можем преобразовать char*
в struct contains_64_t
, поскольку оно правильно выровнено. Но проблема в том, что объявленный тип buf
— это char[2 * sizeof(struct contains_64_t)]
. Таким образом, формально говоря, мы не можем получить доступ к buf
через lvalue типа struct contains_64_t *
.
Но это было бы странно, так как мы правильно выровняли указатель и буквально идентичное представление объекта. Конечно, мы могли бы объявить struct contains_64_t buf[2];
, но решение не сработает, если struct
содержит массив переменной длины
UPD: Будет ли достаточно такого выравнивания буфера, если мы предположим, что компилируем с помощью GCC?
struct contains_64_t *c64_ptr = (struct contains_64_t*) buf
.printf("Value = %lu\n", c64_ptr -> value);
64t;printf("Value = %lu\n", c64_ptr -> value);
это конечно UB с 32-битнойlong
. - person chux - Reinstate Monica   schedule 20.03.2019printf
? Исправлено. - person Some Name   schedule 20.03.2019%lu
дляunsigned long
, не обязательноuint64_t
. - person chux - Reinstate Monica   schedule 20.03.20192)
. Заменил его наPRIu64
, определенный вinttypes.h
. - person Some Name   schedule 20.03.2019struct contains_64_t *c64_ptr = (struct contains_64_t*) buf
, в противном случае Стандарт разрешает такое преобразование на уровне6.2.3.2(p7)
64t;6.2.3.2(p7)
. - person Some Name   schedule 20.03.2019