c – 没有兼容的方式来转换相同大小的签名/无符号

前端之家收集整理的这篇文章主要介绍了c – 没有兼容的方式来转换相同大小的签名/无符号前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我担心我可能会遗漏一些微不足道的东西,但是如果你想保留原始的无符号值,似乎没有实际的安全方式来转换为签名类型.

在reinterpret_cast上,5.2.10没有列出整数到整数的转换,因此没有定义(而static_cast定义没有额外的转换).关于积分转换4.7.3基本上说大型无符号的转换将是实现定义的(因此不可移植).

这似乎有限,因为我们知道,例如,uint64_t应该在任何硬件上安全地转换为int64_t并返回而不改变值.另外,标准布局类型的规则实际上保证了安全转换,如果我们在两种类型之间memcpy而不是assign.

我对么?有没有合理的理由说明为什么不能在整数类型之间重新解释广播?

澄清:肯定签名版本的无符号不保证值,但它只是我考虑的往返(unsigned => signed => unsigned)

更新:仔细查看答案并交叉检查标准,我相信memcpy实际上并不能保证工作,因为它没有说明这两种类型是布局兼容的,也不是char类型.进一步更新,挖掘C标准这个memcpy应该工作,因为目标的大小足够大并且它复制字节.

答案:似乎没有技术上的理由说明为什么不允许reinterpret_cast执行此转换.对于这些固定大小的整数类型,memcpy保证可以工作,实际上只要中间可以表示所有的位模式,任何中间类型都可以使用(浮点数可能很危险,因为可能存在陷阱模式).通常,您不能在任何标准布局类型之间进行memcpy,它们必须是兼容的或char类型.这里的注册是特殊的,因为它们有额外的保证.

解决方法

正如您所指出的,memcpy是安全的:
uint64_t a = 1ull<<63;
int64_t b;
memcpy(&b,&a,sizeof a);

值b仍然是实现定义的,因为C不需要二进制补码表示,但是将其转换回来将给出原始值.

正如Bo PeRSSon所指出的,int64_t将是两个补码.因此,memcpy应该产生一个有符号值,对于该有符号值,简单的积分转换回无符号类型被很好地定义为原始的无符号值.

uint64_t c = b;
assert( a == c );

此外,您可以实现自己的’signed_cast’以简化转换(我不利用这两个补码,因为它们不仅限于intN_t类型):

template<typename T>
typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value,T>::type
signed_cast(typename std::make_unsigned<T>::type v) {
    T s;
    std::memcpy(&s,&v,sizeof v);
    return s;
}

template<typename T>
typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value,T>::type
signed_cast(typename std::make_signed<T>::type v) {
    T s;
    std::memcpy(&s,sizeof v);
    return s;
}

猜你在找的C&C++相关文章