处理C++中的字节序

6gpjuf90  于 2023-05-02  发布在  其他
关注(0)|答案(5)|浏览(169)

我正在把一个系统从Python翻译成C++。我需要能够在c++中执行通常通过使用Python的struct.unpack(将二进制字符串解释为数值)执行的操作。对于整数值,我可以使用stdint.h中的数据类型使其(有点)工作:

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

这对于小端二进制字符串可以正常工作,但对于大端二进制字符串则会失败。基本上,我需要一个在结构中使用>标记的等价物。拆包:

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

请注意,如果有更好的方法来做到这一点,我洗耳恭听。但是,我不能使用c++11,也不能使用Boost以外的任何第三方库。我还需要能够解释浮点数和双精度数,就像在struct.unpack(">f", str)struct.unpack(">d", str)中一样,但我将在解决这个问题时解决这个问题。

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

xjreopfe

xjreopfe1#

对于32位和16位值:

这正是网络数据的问题所在,它是big-endian。您可以使用ntohl将32位转换为主机顺序,在您的情况下是little-endian。
函数的作用是:将无符号整数netlong从网络字节顺序转换为主机字节顺序。

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

这也将照顾到的情况下,你的主机是big-endian,不会做任何事情。

对于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位的值中,将其移位,然后进行翻译。我会写一些好的测试。我怀疑这是一个不寻常的情况,更多的细节将有所帮助。

olmpazwi

olmpazwi2#

一次解压缩一个字节的字符串。

unsigned char *str;
unsigned int result;

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

ej83mcc03#

首先,你正在做的事情:

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*()(其中*为空)、ls(支持不同类型)。由于不同的主机可能有不同的字节顺序,因此如果您正在阅读的数据在所有平台上使用一致的序列化形式,那么这可能就是您想要使用的。

ntoh(i);

您还可以在将str复制到整数之前手动移动字节。

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

也可以使用移位和按位运算符手动操作整数的值。

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

w80xi6nr4#

这福尔斯无聊的范畴。

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;
}
uubf1zoe

uubf1zoe5#

如果你接收到的值是真正的字符串(char* 或std::string),并且你知道它们的格式信息,sscanf()和atoi(),那么,ato()真的会是你的朋友。它们接受格式良好的字符串,并根据传入的格式转换它们(类似于反向printf)。

相关问题