Переосмысление соответствующим образом выровненных указателей на объект с объявленным типом - PullRequest
1 голос
/ 20 марта 2019

Стандарт позволяет нам приводить указатели на тип объекта друг к другу, если они правильно выровнены. 6.3.2.3(p7)

Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. Если результирующий указатель не правильно align68) для ссылочного типа поведение не определено.

Стандарт позволяет нам копировать представление объекта в 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?

1 Ответ

2 голосов
/ 20 марта 2019

memcpy() выглядит нормально.

c64_ptr -> value - UB.

Объект должен иметь свое сохраненное значение, доступное только через lvalue выражение, которое имеет один из следующих типов:

- тип, совместимый с эффективным типом объекта,

- квалифицированная версия типа, совместимого с эффективным типом объект,

- тип, который является типом со знаком или без знака, соответствующим эффективный тип объекта,

- тип, который является типом со знаком или без знака, соответствующим квалифицированная версия действующего типа объекта,

- совокупный или объединенный тип, который включает в себя один из вышеупомянутых типы среди его членов (включая, рекурсивно, член субагрегат или объединенный союз), или

- тип символа.

Посмотрите на compatible в стандарте, чтобы завершить картину.

...