Страницы

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

суббота, 1 февраля 2020 г.

Безопасно ли получать адрес члена невыровненной структуры

#cpp #c #выравнивание


Меня интересует следующий вопрос:

struct S
{
    // ...
    int i;
};

void f(uint8_t *_buf, size_t _offset)
{
    uint8_t *pu = _buf + _offset;
    S *ps = (S*)pu;
    int *pi = &(ps->i);// UB or not UB?

    int i;
    memcpy(&i, pi, sizeof(int));
}


У нас есть буфер байтов и смещение. По указанному смещению в буфере лежит объект
типа S. Этот объект имеет неверное выравнивание, как и поле i этого объекта.

Меня интересует, можно ли получить адрес поля i описанным образом, чтобы потом считать
int с указанного адреса безопасным способом - по одному байту или при помощи memcpy(). 
    


Ответы

Ответ 1



C99 6.3.2.3/7 A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined. *Otherwise... Вольный перевод: Указатель на объект или неполный тип может быть преобразован в указатель на другой объект или неполный тип. Если результирующий указатель не выровнен корректно для типа, на который он будет указывать, поведение неопределено. Иначе... Т.е. со строгих слов стандарта Си получение любого не выровненного указателя, не зависимо от того, будет ли по нему доступ к памяти или нет — UB.

Ответ 2



Для этого будет достаточно взять смещение этого поля от начала структуры без каких-либо промежуточных указателей (подразумевается, что структура является standard layout type): ::std::memcpy(::std::addressof(i), _buf + _offset + offsetof(S, i), sizeof(i)); Что касается попытки преобразования указателя на объект с менее строгим выравниванием к указателю на объект с более строгим выравниванием, то в С++ поведение не специфицировано (то бишь задается реализацией): 8.5.1.10 Reinterpret cast [expr.reinterpret.cast] 7 An object pointer can be explicitly converted to an object pointer of a different type. 73 When a prvalue v of object pointer type is converted to the object pointer type “pointer to cv T ”, the result is static_cast(static_cast(v)). [Note: Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. —end note ] 8.5.1.9 Static cast [expr.static.cast] 13 A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T”, where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. If the original pointer value represents the address A of a byte in memory and A does not satisfy the alignment requirement of T, then the resulting pointer value is unspecified. Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible (6.7.2) with a, the result is a pointer to b. Otherwise, the pointer value is unchanged by the conversion. Но вот использовать полученный указатель на структуру (даже если реализация правильно производит преобразование адреса) для получения указателя на поле этой структуры уже будет неопределенным поведением.

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

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