不管值大小如何,一次读取一个字节的字节序是否不可知?

时间:2019-05-30 20:27:25

标签: c endianness

说我正在向流中读写uint32_t值。如果我像下面的示例一样每次向一个流读/写一个字节并移动每个字节,那么无论机器字节序如何,结果是否一致?

在此处的示例中,流是名为p的内存中的缓冲区。

static uint32_t s_read_uint32(uint8_t** p)
{
    uint32_t value;
    value  = (*p)[0];
    value |= (((uint32_t)((*p)[1])) << 8);
    value |= (((uint32_t)((*p)[2])) << 16);
    value |= (((uint32_t)((*p)[3])) << 24);
    *p += 4;
    return value;
}

static void s_write_uint32(uint8_t** p, uint32_t value)
{
    (*p)[0] = value & 0xFF;
    (*p)[1] = (value >> 8 ) & 0xFF;
    (*p)[2] = (value >> 16) & 0xFF;
    (*p)[3] = value >> 24;
    *p += 4;
}

我目前无法使用big-endian机器进行测试,但是这个想法是,如果每个字节一次写入一个字节,那么每个字节都可以独立地写入流中或从流中读取。然后,CPU可以通过将这些细节隐藏在移位操作之后来处理字节序。是真的,如果没有人请解释为什么?

2 个答案:

答案 0 :(得分:6)

  

如果我像下面的示例那样一次向一个流中读取/写入一个字节并移位每个字节,那么无论机器字节序如何,结果是否一致?

是的。您的s_write_uint32()函数以从最低有效到最高有效的顺序存储输入值的字节,而不管该值在本机表示形式中的顺序。无论s_read_uint32()的基本表示如何,您的uint32_t都会正确地逆转此过程。这些工作是因为

  • 移位运算符(<<>>)的行为是根据左操作数的 value 而不是其表示形式来定义的
  • & 0xff屏蔽掉左操作数的所有位,但屏蔽其最低有效字节的所有位,而与值的表示形式无关(因为0xff具有匹配的表示形式),并且
  • |=操作只是将字节放入结果中;通过前面的左移适当地选择位置。如果改为使用+=,则可能会更清楚,但结果没有什么不同。

但是请注意,在某种程度上,您是在重新发明轮子。 POSIX定义了一个功能对htonl()nothl()(在许多非POSIX系统上也受支持),用于处理四字节数字中的字节顺序问题。这个想法是,发送时,每个人都使用htonl()从主机字节顺序(无论是字节顺序)转换为网络字节顺序(大字节序),然后发送所得的四字节缓冲区。收到后,每个人都将四个字节合并为一个字节,然后使用ntohl()从网络转换为主机字节顺序。

答案 1 :(得分:1)

它可以工作,但是在写功能上需要memcpy,然后附加条件字节交换will give you much better codegen

#include <stdint.h>
#include <string.h>

#define LE (((char*)&(uint_least32_t){1})[0]) // little endian ? 
void byteswap(char*,size_t);

uint32_t s2_read_uint32(uint8_t** p)
{
    uint32_t value;
    memcpy(&value,*p,sizeof(value));
    if(!LE) byteswap(&value,4);
    return *p+=4, value;
}

 void s2_write_uint32(uint8_t** p, uint32_t value)
{
    memcpy(*p,&value,sizeof(value));
    if(!LE) byteswap(*p,4);
    *p+=4;
}

从第8个系列开始的Gcc(但不是clang)可以消除在小端平台上的这种变化,但是您应该通过restrict限定指向目标的双向间接指针来帮助它,否则可能会认为对(*p)[0]的写操作可能会使*p无效(uint8_t是一个char类型,因此允许为任何别名)。

void s_write_uint32(uint8_t** restrict p, uint32_t value)
{
    (*p)[0] = value & 0xFF;
    (*p)[1] = (value >> 8 ) & 0xFF;
    (*p)[2] = (value >> 16) & 0xFF;
    (*p)[3] = value >> 24;
    *p += 4;
}