将有符号整数按位复制为无符号整数的有效方法

/* [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 

警告:正如评论中所讨论的,这似乎在CC未定义行为中是可以的,因为你的问题有两个标签我将在这里留下。 有关更多信息,请查看此问题:

访问非活动的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)。