Предпочтительная идиома для чтения, не зависящего от порядка байтов
В исходном коде Plan 9 я часто нахожу такой код для чтения сериализованных данных из буфера с четко определенным порядком байтов:
#include <stdint.h>
uint32_t le32read(uint8_t buf[static 4]) {
return (buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24);
}
Я ожидал, что и gcc, и clang скомпилируют этот код во что-то такое же простое, как эта сборка на amd64:
.global le32read
.type le32read,@function
le32read:
mov (%rdi),%eax
ret
.size le32read,.-le32read
Но вопреки моим ожиданиям, ни gcc, ни clang не распознают этот шаблон и вместо этого производят сложную сборку с несколькими сменами.
Существует ли идиома для такого рода операций, которая переносима на все реализации C99 и дает хороший (т. Е. Подобный представленному выше) код в разных реализациях?
3 ответа
После некоторых исследований я обнаружил (с помощью потрясающих людей в ##c на Freenode), что gcc 5.0 будет реализовывать оптимизацию для шаблона, описанного выше. Фактически, он компилирует источник C, указанный в моем вопросе, в точную сборку, указанную ниже.
Я не нашел похожую информацию о Clang, поэтому я подал отчет об ошибке.
Если вы хотите обеспечить преобразование между порядком собственной платформы и определенным порядком (например, порядком в сети), вы можете позволить системным библиотекам работать и просто использовать функции <netinet/in.h>
: hton, htons, htonl и ntoh, ntohs, nthol.
Но я должен признать, что включаемый файл не гарантирован: под Windows я думаю, что это winsock.h
,
Вы можете определить порядок байтов, как в этом ответе. Затем используйте O32_HOST_ORDER
макрос, чтобы решить, следует ли привести массив байтов к uint32_t
прямо или использовать ваше выражение сдвига бит.
#include <stdint.h>
uint32_t le32read(uint8_t buf[static 4]) {
if (O32_HOST_ORDER == O32_LITTLE_ENDIAN) {
return *(uint32_t *)&buf[0];
}
return (buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24);
}