Я играю с преобразованием с прямым порядком байтов/обратным порядком байтов и нашел кое-что, что немного сбивает с толку, но также интересно.
В первом примере нет проблем с использованием сдвига битов для преобразования порядка байтов для типа uint32_t
. По сути, он приводит целое число uint32_t
к массиву uint8_t
и пытается получить доступ к каждому байту и сдвигу битов.
Пример №1:
uint32_t htonl(uint32_t x)
{
uint8_t *s = (uint8_t*)&x;
return (uint32_t)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]);
}
Однако, если я попытаюсь сделать что-то подобное для uint64_t
ниже, компилятор выдаст предупреждение о том, что ширина [0] меньше 56 бит, как в примере № 2 ниже.
Пример №2:
uint64_t htonl(uint64_t x)
{
uint8_t *s = (uint8_t*)&x;
return (uint64_t)(s[0] << 56 ......);
}
Чтобы заставить его работать, я должен извлечь каждый байт в uint64_t
, чтобы я мог выполнять битовый сдвиг без каких-либо ошибок, как в примере № 3 ниже.
Пример №3:
uint64_t htonll2(uint64_t x)
{
uint64_t byte1 = x & 0xff00000000000000;
uint64_t byte2 = x & 0x00ff000000000000;
uint64_t byte3 = x & 0x0000ff0000000000;
uint64_t byte4 = x & 0x000000ff00000000;
uint64_t byte5 = x & 0x00000000ff000000;
uint64_t byte6 = x & 0x0000000000ff0000;
uint64_t byte7 = x & 0x000000000000ff00;
uint64_t byte8 = x & 0x00000000000000ff;
return (uint64_t)(byte1 >> 56 | byte2 >> 40 | byte3 >> 24 | byte4 >> 8 |
byte5 << 8 | byte6 << 24 | byte7 << 40 | byte8 << 56);
}
Меня немного смущают Example #1
и Example #2
, насколько я понимаю, оба s[i]
имеют размер uint8_t
, но почему-то если сдвигается только на 32 бита или меньше, то вообще нет проблем, но есть проблема при сдвиге вроде 56 биты. Я запускаю эту программу на Ubuntu с GCC 8.3.0.
В этом случае компилятор неявно преобразует s[i]
в 32-битные числа? sizeof(s[0])
равно 1, когда я добавил к этому отладочные сообщения.