将有符号整数按位复制为无符号整数的有效方法
/* [1] */ int i = -1; unsigned u = (unsigned)i; /* [2] */ int i = -1; unsigned u; memcpy(&u, &i, sizeof i); /* [3] */ int i = -1; unsigned u = *(unsigned *)&i;
为了将有符号整数进行位复制到其未签名的伙伴, [1]
应该适用于大多数机器,但据我所知,它不是保证行为。
[2]
应该完全符合我的要求,但我想避免调用库函数的开销。
那么[3]
怎么样? 它能有效地达到我想要的目的吗?
[3]在C和C ++中都是正确的(从C ++ 14开始,但之前没有); 在这种情况下,不需要使用memcpy
。 (也就是说,没有理由不使用memcpy
,因为它有效地传达了你的意图, 显然是安全的,并且没有开销。)
C, 6.5表达式 :
7 – 对象的存储值只能由具有以下类型之一的左值表达式访问:[…]
- 与对象的有效类型对应的有符号或无符号类型,[…]
C ++, [basic.lval] :
10 – 如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:[…]
- 与对象的动态类型对应的有符号或无符号类型,[…]
如您所见,两个标准中的措辞非常相似,因此可以依赖两种语言。
/* [4] */ union unsigned_integer { int i; unsigned u; }; unsigned_integer ui; ui.i = -1; // You now have access to ui.u
警告:正如评论中所讨论的,这似乎在C
和C
未定义行为中是可以的,因为你的问题有两个标签我将在这里留下。 有关更多信息,请查看此问题:
访问非活动的union成员和未定义的行为?
然后我会建议在C++
使用reinterpret_cast
:
/* [5] */ int i = -1; unsigned u = reinterpret_cast(i);
/* [1] */ int i = -1; unsigned u = (unsigned)i;
↑这保证不适用于符号和1的补码机器,因为转换为无符号可保证产生有符号值2 n ,其中n是无符号类型中的值表示位数。 即转换保证产生与签名类型使用二进制补码表示相同的结果。
/* [2] */ int i = -1; unsigned u; memcpy(&u, &i, sizeof i);
↑这样可以很好地工作,因为类型保证具有相同的大小。
/* [3] */ int i = -1; unsigned u = *(unsigned *)&i;
↑这是C ++ 11及更早版本中的正式未定义行为,但它是标准中“严格别名”子句中包含的案例之一,因此每个现存的编译器都可能支持它。 此外,它是reinterpret_cast
所针对的一个例子。 在C ++ 14及更高版本中,关于未定义行为的语言已从(1)关于左值到右值转换的部分中删除。
如果我这样做,我将使用命名的C ++强制转换为清晰。
然而,我会尝试一下有时看起来很标准 – 允许我做什么不切实际的事情编译器必须说些什么,特别是g ++具有严格的别名选项,无论它是什么,但也铿锵因为它被设计为g ++的直接替代品。
至少如果我计划使用那些编译器和选项的代码。
1) [conv.lval],C ++ 11和C ++ 14中的§4.1/ 1。
这是来自N3797文件的第4.7段“整体转换”,这是C ++ 14标准的最新工作草案:
如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模2 n ,其中n是用于表示无符号类型的位数)。 [注意:在二进制补码表示中,此转换是概念性的,并且位模式没有变化(如果没有截断)。 – 尾注]
首先,世界上所有计算机都使用二进制补码表示。 所以[1]是要走的路(除非你将C ++移植到IBM 7090)。