对于endianess-agnostic读取的首选习语

时间:2014-08-09 14:33:35

标签: c optimization endianness idioms

在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 个答案:

答案 0 :(得分:5)

经过一些研究,我发现(在Freenode上## c中的了不起的人的帮助下),gcc 5.0将实现上述模式的优化。实际上,它将我的问题中列出的C源编译为下面列出的精确汇编。

我还没有找到关于clang的类似信息,因此我提交了bug report

答案 1 :(得分:1)

如果您想保证本机平台订单和已定义订单(例如网络订单)之间的转换,您可以让系统库工作,只需使用<netinet/in.h>:hton,htons的功能,htonl和ntoh,ntohs,nthol。

但我必须承认包含文件无法保证:在Windows下我认为它是winsock.h

答案 2 :(得分:-2)

您可以像this answer一样确定结束。然后使用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);
}