Есть массив с 4 байтами, как их поместит в uint32?
uint8 arr[4] = {0xDD, 0xFF, 0xCC, 0xBB};
uint32 ret = ...;
И провести обратную операцию:
uint32 i = 0xDDFFCCBB;
uint8 arr[4] = ...;
Если бы они у вас оба были с одним порядком байт - не было бы проблем:
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;
Если бы они у вас оба были с одним порядком байт - не было бы проблем Проблемы есть — это неопределённое поведение, т.к. нарушено strict aliasing rule, и могут быть нарушены требования по выравниванию. arr[1] << 16 На системах с 16-битным типом int данный сдвиг также вызывает неопределённое поведение.
– wololo
Aug 20 '22 at 17:39
так и хочется попросить показать хоть один компьютер, где это реально не будет работать Ну, допустим, вот такой пример.
– wololo
Aug 22 '22 at 11:57
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
Есть другой способ - использование объединений. Порядок байт в этом случае не имеет значения. Объединения в 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: разбить его на байты можно таким же способом. Вот моя статья, где всё подробно описано.
Дело в том, что C запрещает писать в одни поля union, а читать из других. http://stackoverflow.com/questions/2906365/gcc-strict-aliasing-and-casting-through-a-union
– gbg Nov 02 '16 at 10:29Порядок байт в этом случае не имеет значения. Не понятно, с чего это вдруг порядок байт перестал иметь значение? Ведь даже в вашем примере на некоторых системах в ret будет записано вовсе не 0xDDFFCCBB.
– wololo
Aug 20 '22 at 17:30
В свое время мне нравилось вот такое решение:
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
uint8_t псевдонимом не типа unsigned char, а некоего иного встроенного __UINT_8_T__, и мы автоматически получим неопределённое поведение из-за нарушения strict aliasing rule.
– wololo
Aug 20 '22 at 17:49
ret = *(uint32*)arr;работает? – KoVadim Nov 01 '16 at 08:42