Объясните, пожалуйста, как использовать битовый сдвиг для того, чтобы из четырёх переменных unsigned char получить одну unsigned int, затем проделать обратное действие.
- 11,528
- 1,497
4 Answers
Последовательно в младшую часть int кладем очередной байт и сдвигаем влево на 8 бит. Что бы положить 1 байт в младшую часть используем логическое ИЛИ:
unsigned char c1=5,c2=10,c3=98,c4=67;
unsigned int I;
I=c4; // c4 в младших 8и битах, остальные 0
I<<=8; // Сдвигаем int влево на 8 бит. Младшие 8 бит становятся 0, c4 становится в 9-16 битах.
I|=c3; // Логическое ИЛИ заменяет 0 биты на те, что в байте c3
I<<=8; I|=c2; I<<=8; I|=c1; // Аналогично кладем остальные байты
Для обратного действия сдвигаем int так, что бы нужный нам байт был самым младшим и маскируем остальные биты логическим И:
c1=I & 0xFF;
c2=(I>>8) & 0xFF;
c3=(I>>16) & 0xFF;
c4=(I>>24) & 0xFF;
Только следим за порядком байт, на разных архитектурах числа в int принято класть по разному.
- 44,087
Если вам нужно делать только такую операцию упаковки/распаковки то могу предложить вообще не используя битовые операции.
unsigned int UI = 0x12345678;
char *CC = (char *)(&UI);
for (int i=3;i>=0;i--)
cout << (int)CC[i]<<" ";
unsigned char NC[4] = {120,86,52,18};
unsigned int TI = *(unsigned int *)(NC);
cout << TI;
Идея основана на явном преобразовании указателя. https://ideone.com/1P4tfc запускаемый пример.
- 9,864
-
2@Abyx, по крайней мере на на x86 в g++ это работать будет (да и трудно предположить, что найдется реальный компилятор, который нарушит тут (вот в этом коде) выравнивание (с чего бы ему вставлять паддинг в 1, 2 или 3 байта в стеке перед NC[4]?)) А strict aliasing (несмотря на предупреждения стандарта) в данном случае imho перебор. – avp Apr 05 '16 at 21:20
Например, вот так:
int main()
{
unsigned char ch1 = 0x1;
unsigned char ch2 = 0x2;
unsigned char ch3 = 0x3;
unsigned char ch4 = 0x4;
unsigned int value = ch1;
value <<= 8;
value |= ch2;
value <<= 8;
value |= ch3;
value <<= 8;
value |= ch4;
}
Или чуть более общий вариант решения:
#include <array>
const size_t BYTE_COUNT_IN_INT = 4;
const size_t BITS_COUNT_IN_BYTE = 8;
unsigned int getIntFromCharsArray(const std::array<unsigned char, BYTE_COUNT_IN_INT>& charsArray)
{
unsigned int result = charsArray[0];
for (size_t i = 1; i <= BYTE_COUNT_IN_INT - 1; ++i)
{
result <<= BITS_COUNT_IN_BYTE;
result |= charsArray[i];
}
return result;
}
std::array<unsigned char, BYTE_COUNT_IN_INT> getCharsArrayFromInt(unsigned int value)
{
std::array<unsigned char, BYTE_COUNT_IN_INT> result;
for (size_t i = 0; i <= BYTE_COUNT_IN_INT - 1; ++i)
{
result[BYTE_COUNT_IN_INT - i - 1] = value;
value >>= BITS_COUNT_IN_BYTE;
}
return result;
}
int main()
{
const std::array<unsigned char, BYTE_COUNT_IN_INT> charsArray = { 0x1, 0x2, 0x3, 0x4 };
unsigned int intFromCharsArray = getIntFromCharsArray(charsArray);
auto charsArrayFromInt = getCharsArrayFromInt(intFromCharsArray);
}
- 7,759
-
@Abyx не надо писать 'не правильно', это неправильно. :) Про 'void' согласен, наследие MS. – αλεχολυτ Apr 06 '16 at 04:55
Дополню. Если нужна именно конвертация и можно не использовать битовые сдвиги, то самое простое решение через union:
union converter
{
unsigned int number;
unsigned char bytes[4];
};
Число в байты:
converter c;
c.number = 123;
// c.bytes[0] == 0xD2
// c.bytes[1] == 0x04
// c.bytes[2] == 0x00
// c.bytes[3] == 0x00
Байты в число:
converter c;
c.bytes[0] = 1;
c.bytes[1] = 2;
c.bytes[2] = 3;
c.bytes[3] = 4;
// c.number == 0x04030201 (big-endian)
- 2,465
BIG_ENDIAN/LITTLE_ENDIAN)? Это ведь отвечает лишь за порядок, в котором представлены байты в памяти машины, а при работе сintэто не имеет значения (байты всегда развернутся в одинаковом порядке), мы ведь не накладываем переменную типаint, на какую-то область памяти по её адресу. Разве нет? – StateItPrimitive Apr 06 '16 at 09:50& 0xFF? Можно просто(unsigned char)(I>>8)и т. д. И хотя не глупый компилятор сгенерирует одинаковый код в обоих случаях, я всё же не уверен, что все компиляторы не глупые и поймут, что побитовое И можно не делать. – Zealint Apr 07 '16 at 12:08