我有二进制的,大尾数形式的消息,例如:
struct Msg
{
// 4 byte int
// 6 byte int
// 2 byte int
};
我希望读取6个字节的整数。
我之前没有遇到过一个不是1、2、4或8个字节的成员。
我不关心可移植性,您可以假定这是在使用GCC编译器的Linux系统上。
要阅读此书,我应该这样做:
struct Msg
{
uint32_t a;
uint16_t b; // Part of 6 byte field
uint32_t c; // Part of 6 byte field
uint16_t d;
}
然后我reinterpret_cast
整条消息,将b
和c
的字节交换(转换为小尾数),然后将b
的值乘以将FFFF
添加到c
之前?
Msg msg = *reinterpret_cast<Msg*>(&bytes[0]);
value = (__builtin_bswap16(msg.b) << 32) + __builtin_bswap32(msg.c);
答案 0 :(得分:3)
我认为编写一个可以转换任意大小的int
的函数更加简单和容易:
uint64_t readBigEndInt( const unsigned char *buff, size_t size )
{
uint64_t r = 0;
while( size-- )
r = (r << 8) + *buff++;
return r;
}
,然后将其相应地应用于原始缓冲区的一部分。
还要感谢@MSalters,如果您关心速度,请制作此功能模板:
template<size_t size>
uint64_t readBigEndInt( const unsigned char *buff )
{
uint64_t r = 0;
for( size_t i = 0; i < size; ++i )
r = (r << 8) + *buff++;
return r;
}
因此编译器可以进行更好的优化,并且可以在必要时为1,2,4和8个字节提供特殊化。另外,我会将其包装到表示流的瘦类中,该类保留当前位置并通过读取操作将其移动。因此,您的代码结尾将类似于:
stream s(buffer,size);
uint32_t a = s.read<4>();
uint64_t b = s.read<6>();
uint16_t c = s.read<2>();
以此类推。
答案 1 :(得分:1)
到目前为止,最简单的不可移植的解决方案是将6 + 2个字节成员读入单个std::uint64_t
中,提取低16位并将其字节交换,然后对其余6个字节进行字节交换(它将整齐地向下移动到低48位)
记住在提取低16位后将其清零。
答案 2 :(得分:0)
唯一的便携式处理方式如Kamil在评论中所建议的那样,使用正确大小的char
数组,并从正确的偏移量中手动提取字段。如果您做得足够值得,肯定可以编写包装器机制,以使其变得更好。
要使结构方法正确工作,您需要一种不可移植的#pragma pack
或__attribute__((packed))
或其他特定于编译器的方法,以防止成员的正常填充和对齐。