说我有一个字节数组:
std::array<std::uint8_t, 4> list
,我想将list
中包含的位连接起来后,将它们转换为它们对应的有符号值。例如,对于list
,由于它是一个大小为4的数组,这将转换为int32
。在C ++中这样做不会导致未定义或特定于编译器的行为的“正确”方法是什么?这样做是正确的,而不是未定义或特定于编译器的吗?
std::uint32_t sum = list[0];
sum = sum + static_cast<std::uint32_t>(list[1])<<8;
sum = sum + static_cast<std::uint32_t>(list[2])<<16;
sum = sum + static_cast<std::uint32_t>(list[3])<<24;
std::int32_t sum_int32 = static_cast<std::int32_t>(sum);
换句话说,sum是指将值的32位表示保留为二进制补码。
答案 0 :(得分:3)
如果你坚持
将其转换为其相应签名值
即为int32_t 比以下代码应该快速,安全,简短,可移植且易于使用,因为使用的boost代码仅是标头(无需构建boost):
#include <array>
#include <iostream>
#include <boost/numeric/conversion/cast.hpp>
#ifdef __linux__
#include <arpa/inet.h>
#elif _WIN32
#include <winsock.h>
#else
// ...
#endif
int main()
{
using boost::numeric_cast;
using boost::numeric::bad_numeric_cast;
using boost::numeric::positive_overflow;
using boost::numeric::negative_overflow;
std::array<uint8_t, 4> a = { 1,2,3,4 }; // big-endian
uint32_t ui = ntohl(*reinterpret_cast<uint32_t*>(a.data())); // convert to host specific byte order
std::cout << std::hex << ui << std::endl;
try
{
int32_t si = numeric_cast<int32_t>(ui); // This conversion succeeds (is in range)
std::cout << std::hex << si << std::endl;
}
catch (negative_overflow& e) {
std::cout << e.what();
}
catch (positive_overflow& e) {
std::cout << e.what();
}
}
答案 1 :(得分:0)
很好,但是如果未签名的值不能用已签名的值表示(例如,参见this在线c ++标准草案),则会得到实现定义的行为:
4.7整体转化
3)如果目标类型是带符号的,则该值可以保持不变 以目标类型(和位域宽度)表示; 否则,该值是实现定义的。
要解决此问题,您可以将最高有效字节限制为7位:
sum = sum + (static_cast<std::uint32_t>(list[3]) & 0x7F) <<24;
if (static_cast<std::uint32_t>(list[3]) & 0x80) {
sum = -sum;
}
请注意,只有31位可用于有符号值的内容。
您实际上不能依靠二进制补码表示,但是使用sum = -sum
表示法应该很安全。