我试图将一个字节数组转换为long
long readAndSkipLong(char*& b)
{
unsigned long ret = (b[0] << 56) | (b[1] << 48) | (b[2] << 40) | (b[3]<<32) | (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | (b[7]);
return ret;
}
我的转变似乎不对。对于预期的价值
152 --> 00000000 00000000 00000000 00000000 00000000 00000000 00000000 10011000
我明白了:
-104 --> 11111111 11111111 11111111 11111111 11111111 11111111 11111111 10011000
知道bug在哪里?
这是因为类型促销和标志扩展。 char
数组中的每个值都是有符号的,并且位移是一个整数运算。当你使用移位运算符时,它会计算为int
,并且因为你的char
s是有符号的,所以移位它们会产生签名的int
s。
最后(最右边)的字节有1
作为符号位。当晋升为int
时,其值通过符号扩展变为-104
。当您对其余数字进行OR运算时,所有1
位都不受影响。
为避免此问题,您可以在移位和ORing之前将每个char
s转换为unsigned long
。
你可以做的另一件事就是用char
和0xff
对每个((b[i] & 0xff) << 24)
进行逐位AND运算。与0xff
进行AND运会产生一个int
,保持最不重要的8位完整并且向左零,没有符号扩展。
2件事:
char
can be signed or unsigned,因此不应该用于存储除字符以外的数据类型。
在C,C ++和大多数类C语言中,任何类型都比表达式中的int
must be promoted to int
更窄,并且您的语句将被视为这样
unsigned long ret = ((int)b[0] << 56) | ((int)b[1] << 48)
| ((int)b[2] << 40) | ((int)b[3] << 32)
| ((int)b[4] << 24) | ((int)b[5] << 16)
| ((int)b[6] << 8) | ((int)b[7]);
如果char
签名,它将使用符号扩展名提升为int
。因此,如果字节值为负,则顶部位将填充1。
在MSVC中,char
默认签名。您可以使用/J
使char无符号,这将解决您的部分问题。但随后又出现了另一个问题:long
is a 32-bit type中,因此您无法将8个字节打包到其中。此外int
在大多数现代系统上也是32位,并且在将b[i]
推广到int shifting more than 31 is undefined behavior之后,这是你的程序所做的。因此,为了便于解决所有问题,您需要:
b[i]
转换为unsigned char
或uint8_t
,或者通过与0xFF进行AND运算来掩盖高位,如0605002建议的那样。或者只是将b
的类型更改为unsigned char&*
而不是char&*
long long
,int64_t
或int_least64_t
结果可能如下所示
long long readAndSkipLong(unsigned char*& b)
{
return ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48)
| ((uint64_t)b[2] << 40) | ((uint64_t)b[3] << 32)
| ((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16)
| ((uint64_t)b[6] << 8) | ((uint64_t)b[7]);
}
但是,在x86上通常不允许进行未对齐访问,因此您可以简单地将该函数替换为
ntohll(*(int64_t*)&b);
有几件事要考虑 -
这是我在一个小端机器上为字节写入uint64_t的一些代码。
std::uint64_t bytesToUint64(std::uint8_t* b) {
std::uint64_t msb = 0x0u;
for (int i(0); i < 7; i++) {
msb |= b[i];
msb <<= 8;
}
msb |= b[7];
return msb;
}
OP编辑(实施提示1):
long readAndSkipLong(char*& b)
{
std::uint64_t ret =
((std::uint8_t)b[0] << 56) |
((std::uint8_t)b[1] << 48) |
((std::uint8_t)b[2] << 40) |
((std::uint8_t)b[3] << 32) |
((std::uint8_t)b[4] << 24) |
((std::uint8_t)b[5] << 16) |
((std::uint8_t)b[6] << 8) |
((std::uint8_t)b[7]);
b+=8;
return ret;
}