微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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]但是代码应该以完全可移植的方式处理所有值,这些值将在“长”.

原文地址:https://www.jb51.cc/c/118873.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐