说我正在向流中读写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可以通过将这些细节隐藏在移位操作之后来处理字节序。是真的,如果没有人请解释为什么?
答案 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;
}