c – 如何在不调用un或实现定义的行为的情况下从uint8_t的缓冲区中读取有符号整数?

这是一个简单的函数,试图从big-endian缓冲区读取一个通用的二进制补码整数,我们假设std :: is_signed_v< INT_T>:
template<typename INT_T>
INT_T read_big_endian(uint8_t const *data) {
    INT_T result = 0;
    for (size_t i = 0; i < sizeof(INT_T); i++) {
        result <<= 8;
        result |= *data;
        data++;
    }
    return result;
}

不幸的是,这是未定义的行为,因为最后的<< =转移到符号位. 所以现在我们尝试以下方法

template<typename INT_T>
INT_T read_big_endian(uint8_t const *data) {
    std::make_unsigned_t<INT_T> result = 0;
    for (size_t i = 0; i < sizeof(INT_T); i++) {
        result <<= 8;
        result |= *data;
        data++;
    }
    return static_cast<INT_T>(result);
}

但是我们现在在static_cast中调用实现定义的行为,从unsigned转换为signed.

如何在“明确定义”的领域中做到这一点?

解决方法

首先将字节组合成无符号值.除非您需要组装9个或更多个八位字节的组,否则符合C99的实现将保证具有足够大的类型以保存所有这些类型(C89实现将保证具有足够大的无符号类型以容纳至少四个).

在大多数情况下,如果要将八位字节序列转换为数字,您将知道您期望的八位字节数.如果数据被编码为4个字节,则无论int和long的大小如何,都应使用四个字节(便携式函数应返回long类型).

unsigned long octets_to_unsigned32_little_endian(unsigned char *p)
{
  return p[0] | 
    ((unsigned)p[1]<<8) |
    ((unsigned long)p[2]<<16) |
    ((unsigned long)p[3]<<24);
}
long octets_to_signed32_little_endian(unsigned char *p)
{
  unsigned long as_unsigned = octets_to_unsigned32_little_endian(p);
  if (as_unsigned < 0x80000000)
    return as_unsigned;
  else
    return (long)(as_unsigned^0x80000000UL)-0x40000000L-0x40000000L;
}

注意,减法是作为两个部分完成的,每个部分都在有符号长度的范围内,以允许LNG_MIN为-2147483647的系统的可能性.尝试在这样的系统上转换字节序列{0,0×80}可能会产生未定义的行为[因为它会计算值-2147483648]但是代码应该以完全可移植的方式处理所有值,这些值将在“长”.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...