3

Есть массив с 4 байтами, как их поместит в uint32?

uint8 arr[4] = {0xDD, 0xFF, 0xCC, 0xBB};
uint32 ret = ...;

И провести обратную операцию:

uint32 i = 0xDDFFCCBB;
uint8 arr[4] = ...;
Fangog
  • 1,655

3 Answers3

3

Если бы они у вас оба были с одним порядком байт - не было бы проблем:

uint8 arr[4] = {0xDD, 0xFF, 0xCC, 0xBB};
uint32 ret = *(uint32*)arr;

В вашем же варианте придется

ret = arr[3]|(arr[2] << 8)|(arr[1] << 16)|(arr[0] << 24);

Обратно - примерно так же:

arr[3] = ret&0xFF;
arr[2] = (ret >> 8)&0xFF;
arr[1] = (ret >> 16)&0xFF;
arr[0] = (ret >> 24)&0xFF;
Harry
  • 221,325
  • Если бы они у вас оба были с одним порядком байт - не было бы проблем Проблемы есть — это неопределённое поведение, т.к. нарушено strict aliasing rule, и могут быть нарушены требования по выравниванию. arr[1] << 16 На системах с 16-битным типом int данный сдвиг также вызывает неопределённое поведение. – wololo Aug 20 '22 at 17:39
  • 1
    Честно говоря, уже так достал этот strict aliasing, что, признавая вашу правоту, так и хочется попросить показать хоть один компьютер, где это реально не будет работать... – Harry Aug 21 '22 at 04:55
  • так и хочется попросить показать хоть один компьютер, где это реально не будет работать Ну, допустим, вот такой пример. – wololo Aug 22 '22 at 11:57
  • @wololo Я сказал ЭТО, т.е. код из ответа, а не надуманная конструкция типа alignas(int) alignas(char8_t) char8_t arr[sizeof(int)]... Откровенно говоря, такое обилие alignas вызывает тоску и непонимание, что, а главное, зачем и за чем выравнивается... Кстати, VC++ на тот код отвечает -1 255. – Harry Aug 22 '22 at 14:12
  • Хотите сказать работоспособность вашего примера гарантируется только, когда кроме ваших двух строчек кода в программе больше ничего нет?) alignas нужны, чтобы исключить проблемы с выравниванием, но без них тоже не работает. – wololo Aug 22 '22 at 14:18
  • Еще раз и медленно - не придуманный пример, призванный надурить оптимизатор, а код uint8 arr[4] = {0xDD, 0xFF, 0xCC, 0xBB}; uint32 ret = *(uint32*)arr; – Harry Aug 22 '22 at 14:34
  • Ну, вы так и пишите в ответе, что для работоспособности вашего примера, кроме приведённых двух строчек, в программе ничего не должно быть. ¯_(ツ)_/¯ Хотя бы один компьютер, где ЭТО: *(uint32*)arr не работает, я привёл. – wololo Aug 22 '22 at 14:45
  • Бессмысленная дискуссия. И совершенно бессмысленно поднимать ее через почти 6 лет после ответа на вопрос... Как я уже написал - формально вы правы. Надеюсь, этого достаточно? Или вы хотите, чтобы я исправил свой ответ? Но ведь вы имеете возможность вносить правки сами, за чем дело стало? :) – Harry Aug 22 '22 at 15:26
1

Есть другой способ - использование объединений. Порядок байт в этом случае не имеет значения. Объединения в C похожи на структуры с той лишь разницей, что каждый элемент объединения расположен по одному и тому же адресу памяти. В вашем случае это будет выглядеть так:

typedef union {
    uint32 word,
    uint8 bytes[sizeof(uint32)]
} WordChar;

В дальнейшем вы можете использовать тип WordChar следующим образом:

WordChar wc;
wc.bytes[0] = 0xDD;
wc.bytes[1] = 0xFF;
wc.bytes[2] = 0xCC;
wc.bytes[3] = 0xBB;
uint32 ret = wc.word; //0xDDFFCCBB

То же касается типа float: разбить его на байты можно таким же способом. Вот моя статья, где всё подробно описано.

maestro
  • 4,735
  • Все очень плохо с вашим решением. Оно нарушает strict-aliasing и годится для удивления коллег со словами "глянь, ошибка в компеляторе!!!!11"

    Дело в том, что C запрещает писать в одни поля union, а читать из других. http://stackoverflow.com/questions/2906365/gcc-strict-aliasing-and-casting-through-a-union

    – gbg Nov 02 '16 at 10:29
  • @gbg, если нельзя писать в одни поля, а читать из других, то зачем тогда вообще вводили union? – maestro Nov 02 '16 at 11:05
  • К тому же, если бы речь шла о float, то решение Harry уже не сработало бы. Только объединения. – maestro Nov 02 '16 at 11:09
  • @gdb По вашей ссылке Abyx пишет что char и uchar это особый случай. Кто прав? – Cerbo Dec 13 '16 at 17:11
  • Порядок байт в этом случае не имеет значения. Не понятно, с чего это вдруг порядок байт перестал иметь значение? Ведь даже в вашем примере на некоторых системах в ret будет записано вовсе не 0xDDFFCCBB. – wololo Aug 20 '22 at 17:30
0

В свое время мне нравилось вот такое решение:

uint32_t val32 = 0xAABBCCDD;
uint8_t arr[4];
uint8_t arr[0] = ((uint8_t*)&val32)[0];
uint8_t arr[1] = ((uint8_t*)&val32)[1];
uint8_t arr[2] = ((uint8_t*)&val32)[2];
uint8_t arr[3] = ((uint8_t*)&val32)[3];

В результате имеем: arr[0] = 0xDD, arr[1] = 0xCC, arr[2] = 0xBB, arr[3] = 0xAA

  • 1
    Для некоторых платформ даст неверный результат. Пометка для x86/x64 обязательна. Именно поэтому люди пишут более сложные решения. – nick_n_a Aug 19 '22 at 10:44
  • Более того, внутренний параноик подсказывает, что рано или поздно разработчики стандартной библиотеки додумаются в целях повышения быстродействия сделать тип uint8_t псевдонимом не типа unsigned char, а некоего иного встроенного __UINT_8_T__, и мы автоматически получим неопределённое поведение из-за нарушения strict aliasing rule. – wololo Aug 20 '22 at 17:49