2件事:
char
可以是有符号或无符号的,因此不应用于存储字符以外的数据类型。
在 C、C++ 和大多数类似 C 的语言中,任何比表达式中int
必须提升的类型更窄的类型,您的语句都将被这样处理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 填充。
在 MSVCchar
中,默认情况下是签名的。您可以使用/J
使 char 无符号,这将解决您的部分问题。但随之而来的另一个问题是:
在 Windowslong
中是32 位类型,因此您不能将 8 个字节装入其中。此外int
,在大多数现代系统上也是 32 位的,并且在提升b[i]
到 int移位超过 31 之后是未定义的行为,这就是您的程序所做的。
因此,要便携地解决所有问题,您需要:
- 将所有
b[i]
转换为unsigned char
or uint8_t
,或通过与建议的 0xFF 进行与运算来屏蔽高位,如 0605002 建议的。或者只是将类型更改b
为unsigned char&*
而不是char&*
- 将返回类型更改为至少 64 位宽的类型,例如
(unsigned) long long
,(u)int64_t
或(u)int_least64_t
结果可能如下所示
uint64_t 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]);
}
或者
uint64_t readAndSkipLong(char*& b)
{
return ((uint64_t)(uint8_t)b[0] << 56) | ((uint64_t)(uint8_t)b[1] << 48)
| ((uint64_t)(uint8_t)b[2] << 40) | ((uint64_t)(uint8_t)b[3] << 32)
| ((uint64_t)(uint8_t)b[4] << 24) | ((uint64_t)(uint8_t)b[5] << 16)
| ((uint64_t)(uint8_t)b[6] << 8) | ((uint64_t)(uint8_t)b[7]);
}
但是,您实际上并不需要编写函数来反转字节序。已经有ntohll()
并且htonll()
为此目的
reversedEndian = ntohll(originalValue);
如果输入必须是char
数组,则只需将值复制到uint64_t
memcpy(&originalValue, &b, sizeof originalValue);
reversedEndian = ntohll(originalValue);
您可以进一步减少整个事情reversedEndian = ntohll(*(int64_t*)&b);
是否允许严格别名,因为在 x86 上通常允许未对齐访问