在c ++中处理字节序

时间:2012-12-13 16:00:53

标签: c++ python endianness

我正在努力将系统从python转换为c ++。我需要能够在c ++中执行通常使用Python struct.unpack执行的操作(将二进制字符串解释为数值)。对于整数值,我可以使用stdint.h中的数据类型来实现(某种程度)工作:

struct.unpack("i", str) ==> *(int32_t*) str; //str is a char* containing the data

这适用于little-endian二进制字符串,但在big-endian二进制字符串上失败。基本上,我需要使用struct.unpack中的>标记:

struct.unpack(">i", str) ==> ???

请注意,如果有更好的方法,我会全力以赴。但是,我不能使用c ++ 11,也不能使用除Boost之外的任何第三方库。我还需要能够解释浮点数和双精度数,如struct.unpack(">f", str)struct.unpack(">d", str),但是当我解决这个问题时,我会谈到它。

注意我应该指出,在这种情况下,我的机器的字节顺序无关紧要。我知道我在代码中收到的比特流总是大端的,这就是为什么我需要一个始终涵盖大端案例的解决方案。 BoBTFish在评论中指出的文章似乎提供了一个解决方案。

5 个答案:

答案 0 :(得分:7)

对于32位和16位值:

这正是您对网络数据的问题,这是大端的。您可以使用ntohl将32位转换为主机顺序,在您的情况下为little-endian。

  

ntohl()函数将无符号整数netlong从网络字节顺序转换为          主机字节顺序。

int res = ntohl(*((int32_t) str)));

这也将照顾您的主机是大端并且不会做任何事情的情况。

对于64位值

非标准地在linux / BSD上你可以看看64 bit ntohl() in C++?,它指向htobe64

  

这些函数从字节顺序转换整数值的字节编码          当前CPU(“主机”)使用,来自little-endian和big-endian字节          顺序。

对于Windows,请尝试:How do I convert between big-endian and little-endian values in C++?

指向_byteswap_uint64以及16位和32位解决方案以及特定于gcc的__builtin_bswap(32/64)调用。

其他尺寸

大多数系统的值不是16/32/64位长。此时我可能会尝试将其存储在64位值中,将其移位并进行转换。我会写一些好的测试。我怀疑这是一种不常见的情况,更多细节会有所帮助。

答案 1 :(得分:4)

一次解包一个字符串。

unsigned char *str;
unsigned int result;

result =  *str++ << 24;
result |= *str++ << 16;
result |= *str++ << 8;
result |= *str++;

答案 2 :(得分:2)

首先,你正在做的演员:

char *str = ...;
int32_t i = *(int32_t*)str;
由于严格的别名规则,

会导致未定义的行为(除非使用str等内容初始化int32_t x; char *str = (char*)&x;。实际上,强制转换会导致读取不对齐,导致某些平台出现总线错误(崩溃),导致其他平台性能下降。

相反,你应该做的事情如下:

int32_t i;
std::memcpy(&i, c, sizeof(i));

在主机的本机字节排序和独立于主机的排序之间交换字节有许多功能:ntoh*()hton*(),其中*为空,l ,或s支持的不同类型。由于不同的主机可能具有不同的字节顺序,因此如果您正在阅读的数据在所有平台上使用一致的序列化表单,那么这可能是您想要使用的。

ntoh(i);

您还可以在str中手动移动字节,然后再将其复制到整数中。

std::swap(str[0],str[3]);
std::swap(str[1],str[2]);
std::memcpy(&i,str,sizeof(i));

或者您可以使用shift和按位运算符手动操作整数值。

std::memcpy(&i,str,sizeof(i));
i = (i&0xFFFF0000)>>16 | (i&0x0000FFFF)<<16;
i = (i&0xFF00FF00)>>8  | (i&0x00FF00FF)<<8;

答案 3 :(得分:0)

这属于一点点琐事。

for (i=0;i<sizeof(struct foo);i++) dst[i] = src[i ^ mask]; 

其中mask ==(sizeof type -1),如果存储和本机字节序不同。

使用这种技术,可以将结构转换为位掩码:

 struct foo {
    byte a,b;       //  mask = 0,0
    short e;        //  mask = 1,1
    int g;          //  mask = 3,3,3,3,
    double i;       //  mask = 7,7,7,7,7,7,7,7
 } s; // notice that all units must be aligned according their native size

同样,这些掩码可以用每个符号两位编码:(1<<n)-1,这意味着在64位机器中,可以在一个常量中编码32字节大小的结构的必要掩码(1,2,4)和8字节对齐)。

unsigned int mask = 0xffffaa50;  // or zero if the endianness matches
for (i=0;i<16;i++) { 
     dst[i]=src[i ^ ((1<<(mask & 3))-1]; mask>>=2;
}

答案 4 :(得分:-1)

如果您收到的值是真正的字符串,(char *或std :: string)并且您知道它们的格式信息,sscanf()和atoi(),那么真的ato()将是您的朋友。它们采用格式良好的字符串并按照传入的格式(反向printf)进行转换。