Я попал в ловушку, пытаясь упаковать пару переменных в одну переменную длиной 8 байт.
По сути, у меня есть пара коротких предметов, которые имеют небольшой двоичный размер, и мне нужно собрать их вместе, чтобы отправить в класс, который должен быть в состоянии распаковать его обратно.
Итак, я сделал следующее:
typedef unsigned long long PACKAGE; // 8 byte (shows as _int64 in debug)
(sizeof returns '8')
unsigned int dat1 = 25; // 1 byte long max
unsigned int dat2 = 1; // 4 bit long max
unsigned int dat3 = 100; // 2 byte long max
unsigned int dat4 = 200; // 4 byte long max
unsigned int dat5 = 2; // 4 bit long max
Затем я делаю переменную типа PACKAGE, которая пуста (0)
PACKAGE pack = 0;
И я хочу бросить переменные в этот пакет с помощью бинарных операций, я делаю:
pack = (dat1 << 56) | (dat2 << 52) | (dat3 << 36) | (dat4 << 4) | dat5;
это работает только наполовину хорошо, я рассчитал, что я должен получить десятичное значение пакета равным 2526526262902525058
, или же
0010001100010000000001100100000000000000000000000000110010000010
как бинарный, однако я получаю 588254914
, или же
00100011000100000000111011000010
как бинарный
что-то правильное в его хвосте и голове, но средняя часть где-то отсутствует.
И когда это будет сделано, я все еще собираюсь как-то извлечь данные обратно.
Я бы предпочел использовать битовое структура для представления такого типа (также используйте uint64_t
быть уверенным в доступном размере):
union PACKAGE {
struct bits {
uint64_t dat1 : 8; // 1 byte long max
uint64_t dat2 : 4; // 4 bit long max
uint64_t dat3 : 16; // 2 byte long max
uint64_t dat4 : 32; // 4 byte long max
uint64_t dat5 : 4; // 4 bit long max
};
uint64_t whole; // for convenience
};
Как уже упоминалось в Комментарии Вы могли бы даже использовать uint_least64_t
тип данных, чтобы убедиться, что ваша цель поддерживает его (так как наличие uint64_t
является необязательным из текущего стандарта c ++):
union PACKAGE {
struct bits {
uint_least64_t dat1 : 8; // 1 byte long max
uint_least64_t dat2 : 4; // 4 bit long max
uint_least64_t dat3 : 16; // 2 byte long max
uint_least64_t dat4 : 32; // 4 byte long max
uint_least64_t dat5 : 4; // 4 bit long max
};
uint_least64_t whole; // for convenience
};
При условии, что sizeof(unsigned int) != sizeof(unsigned long long)
левый операнд каждого сдвига имеет неправильный тип. Каждая операция сдвига усекается (возможно, до 32 бит).
Попробуйте, например:
typedef unsigned long long PACKAGE; // 8 byte (shows as _int64 in debug)
(sizeof returns '8')
unsigned long long dat1 = 25; // 1 byte long max
unsigned long long dat2 = 1; // 4 bit long max
unsigned long long dat3 = 100; // 2 byte long max
unsigned long long dat4 = 200; // 4 byte long max
unsigned long long dat5 = 2; // 4 bit long max
pack = (dat1 << 56) | (dat2 << 52) | (dat3 << 36) | (dat4 << 4) | dat5;
или же:
typedef unsigned long long PACKAGE; // 8 byte (shows as _int64 in debug)
(sizeof returns '8')
unsigned int dat1 = 25; // 1 byte long max
unsigned int dat2 = 1; // 4 bit long max
unsigned int dat3 = 100; // 2 byte long max
unsigned int dat4 = 200; // 4 byte long max
unsigned int dat5 = 2; // 4 bit long max
pack = ((PACKAGE)dat1 << 56) | ((PACKAGE)dat2 << 52) | ((PACKAGE)dat3 << 36) | ((PACKAGE)dat4 << 4) | (PACKAGE)dat5;
Примечание: Хорошо, в действительности каждая операция сдвига, в которой правый операнд больше, чем размер левого типа, в битах вызывает неопределенное поведение. Типичным неопределенным поведением является усечение, но стандарт допускает любое другое поведение, вплоть до глобальной термоядерной войны.