memcpy/memmove to a union member, does this set the 'active' member?
Важное уточнение: некоторые комментаторы, кажется, думают, что я копирую из профсоюза. Посмотри внимательно наmemcpy
Копирует с адреса старого доброгоuint32_t
, который не содержится в союзе. Также копирую (черезmemcpy
) конкретному члену союза (u.a16
или же&u.x_in_a_union
, а не напрямую ко всему самому союзу (&u
)
C ++ довольно строг в отношении союзов - вы должны читать с члена, только если это был последний член, которому было написано:
9.5 Союзы [class.union][[C ++ 11]] В объединении самое большее один из элементов не статических данных может быть активным в любое время, то есть значение самое большее одного из участников не статических данных может быть сохранено в объединении в любое время.
(Конечно, компилятор не отслеживает, какой элемент активен. Разработчик должен убедиться, что они отслеживают это сами)
Обновление: следующий блок кода является основным вопросом, напрямую отражающим текст в заголовке вопроса. Если этот код в порядке, у меня есть продолжение в отношении других типов, но теперь я понимаю, что этот первый блок кода сам по себе интересен.
#include <cstdint>
uint32_t x = 0x12345678;
union {
double whatever;
uint32_t x_in_a_union; // same type as x
} u;
u.whatever = 3.14;
u.x_in_a_union = x; // surely this is OK, despite involving the inactive member?
std::cout << u.x_in_a_union;
u.whatever = 3.14; // make the double 'active' again
memcpy(&u.x_in_a_union, &x); // same types, so should be OK?
std::cout << u.x_in_a_union; // OK here? What's the active member?
Блок кода непосредственно над этим, вероятно, является основной проблемой в комментариях и ответах. Оглядываясь назад, мне не нужно было смешивать типы в этом вопросе! В основном этоu.a = b
такой же какmemcpy(&u.a,&b, sizeof(b))
, предполагая, что типы идентичны?
Во-первых, относительно простоmemcpy
позволяя нам читатьuint32_t
как массивuint16_t
:
#include <cstdint> # to ensure we have standard versions of these two types
uint32_t x = 0x12345678;
uint16_t a16[2];
static_assert(sizeof(x) == sizeof(a16), "");
std:: memcpy(a16, &x, sizeof(x));
Точное поведение зависит от порядкового номера вашей платформы, и вы должны остерегаться представления ловушек и так далее. Но здесь, как правило, все согласны (я думаю, что обратная связь приветствуется!) О том, что с осторожностью, чтобы избежать проблемных значений, приведенный выше код может быть совершенно нормальным стандартом в правильном контексте на правильной платформе.
(Если у вас есть проблема с приведенным выше кодом, пожалуйста, прокомментируйте или отредактируйте вопрос соответствующим образом. Я хочу быть уверен, что у нас есть не противоречивая версия выше, прежде чем перейти к «интересному» коду ниже.)
Если,и только если, оба блока кода выше не-UB, тогда я хотел бы объединить их следующим образом:
uint32_t x = 0x12345678;
union {
double whatever;
uint16_t a16[2];
} u;
u.whatever = 3.14; // sets the 'active' member
static_assert(sizeof(u.a16) == sizeof(x)); //any other checks I should do?
std:: memcpy(u.a16, &x, sizeof(x));
// what is the 'active member' of u now, after the memcpy?
cout << u.a16[0] << ' ' << u.a16[1] << endl; // i.e. is this OK?
Какой член союза,u.whatever
или жеu.a16
это «активный участник»?
Наконец, мое собственное предположение состоит в том, что причина, по которой мы заботимся об этом на практике, заключается в том, что оптимизирующий компилятор может не заметить, чтоmemcpy
произошло и поэтому делает ложные предположения (но допустимые предположения по стандарту) о том, какой элемент активен и какие типы данных являются «активными», что приводит к ошибкам в отношении псевдонимов. Компилятор может изменить порядокmemcpy
странными способами.Является ли это подходящим резюме того, почему мы заботимся об этом?