如何将uint8列表转换为相应的有符号值?

时间:2019-06-12 21:22:40

标签: c++ c++11

说我有一个字节数组:

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位表示保留为二进制补码。

2 个答案:

答案 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表示法应该很安全。