我担心我可能会遗漏一些微不足道的东西,但如果您希望保留原始的无符号值,则似乎没有实际安全的方式来转换为签名类型。
在reinterpret_cast上,5.2.10没有列出整数到整数的转换,因此没有定义(而static_cast定义没有额外的转换)。关于积分转换4.7.3基本上说大型无符号的转换将是实现定义的(因此不可移植)。
这似乎有限,因为我们知道,例如,uint64_t
应该在任何硬件上安全地转换为int64_t
并返回而不会更改值。如果我们在两种类型之间memcpy
而不是分配,那么标准布局类型的规则实际上保证了安全转换。
我说错了吗?有没有合理的理由说明为什么在整数类型之间不能reinterpret_cast
足够大?
澄清:肯定签名版本的无符号不保证值,但它只是我考虑的往返(unsigned => signed => unsigned)
更新:仔细查看答案并交叉检查标准,我相信memcpy
实际上并不能保证正常工作,因为它没有说明这两种类型是布局兼容,也不是char类型。进一步更新,深入研究C-standard这个memcpy应该可以工作,因为目标的大小足够大并且它复制了字节。
答案:似乎没有技术上的理由说明为什么不允许reinterpret_cast执行此转换。对于这些固定大小的整数类型,memcpy
保证可以工作,实际上只要中间可以表示所有的位模式,任何中间类型都可以使用(浮点数可能很危险,因为可能存在陷阱模式)。通常,您可以在任何标准布局类型之间进行memcpy,它们必须是兼容的或char类型。这里的特征是特殊的,因为它们有额外的保证。
答案 0 :(得分:3)
我们知道你不能将任意位序列转换为浮点数,因为它可能是一个陷阱表示。
是否有任何规则表明签名的整数类型中不存在陷阱表示? (无符号类型不能,因为定义范围的方式,有效值需要所有表示)
有符号的表示也可以包含等价类(例如+0 == -0
),并且可以将这样的类中的值强制转换为规范表示,从而打破往返。
以下是标准中的相关规则(sectin 4.7,[conv.integral]
):
如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模2 n ,其中n是用于表示无符号类型的位数)。 [注意:在二进制补码表示中,此转换是概念性的,并且位模式没有变化(如果没有截断)。 - 结束说明]
如果目标类型已签名,则该值如果可以在目标类型(和位字段宽度)中表示,则不会更改;否则,该值是实现定义的。
如果您的意思是在指针或引用上使用reinterpret_cast
而不是值,则必须处理the strict-aliasing rule。你发现的是this case is expressly allowed。
答案 1 :(得分:2)
正如您所指出的,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,&v,sizeof v);
return s;
}
答案 2 :(得分:1)
据推测,这是不允许的,因为对于具有符号幅度表示的机器,它会违反签名0
映射到未签名0
的最小惊喜原则,而签名-0
将映射到某些其他(可能非常大)的数字。
鉴于存在memcpy
解决方案,我认为标准组织决定不支持这种不直观的映射,可能是因为unsigned-&gt; signed-&gt; unsigned不像指针那样有用 - >整型&GT;指针
答案 3 :(得分:0)
问题基本上是n位无符号可能不在n位有符号类型中具有表示。例如,8位无符号的最大值为256,而8位有符号值必须没有大于128的值(并注意这是硬件实现的无关:任何表示需要一点点符号。)
答案 4 :(得分:-2)
跑步
#include <cstdio>
#include <stdint.h>
using namespace std;
int main()
{
int64_t a = 5;
int64_t aa;
uint64_t b;
double c;
b = *reinterpret_cast<uint64_t *>(&a);
aa = *reinterpret_cast<int64_t *>(&b);
if (a == aa) {
printf("as expected, a == aa\n");
}
c = *reinterpret_cast<double *>(&a);
aa = *reinterpret_cast<int64_t *>(&c);
if (a == aa) {
printf("again, as expected, a == aa\n");
}
printf("what is this I don't even %f.\n", c); // this one should give some undefined behavior here
return 0;
}
无法将其纳入评论。
答案 5 :(得分:-2)
除非我误解了这个问题,否则只需将签名类型放入无符号类型,反之亦然,然后再返回:
#include <iostream>
int main()
{
signed char s = -128;
unsigned char u = s;
signed char back = u;
std::cout << (int)u << std::endl;
std::cout << (int)back << std::endl;
return 0;
}
./a.out
128
-128