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

时间:2015-05-05 08:45:38

标签: c++ c

/* [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]怎么样?它能有效地达到我想要的目的吗?

4 个答案:

答案 0 :(得分:10)

checkmark

警告:正如评论中所讨论的那样,/* [4] */ union unsigned_integer { int i; unsigned u; }; unsigned_integer ui; ui.i = -1; // You now have access to ui.u C中的未定义行为似乎没问题,因为您的问题同时包含两个标记我都会这样做留在这里。有关更多信息,请查看此问题:

Accessing inactive union member and undefined behavior?

然后我会在C++中为reinterpret_cast提供建议:

C++

答案 1 :(得分:9)

/* [1] */
int i = -1;
unsigned u = (unsigned)i;

↑这保证在符号和1或补码机器上工作,因为转换为无符号保证产生有符号值2 n < / em> 其中 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 ++及其严格的别名选项,无论它是什么,但也是clang,因为它被设计为g ++的直接替代品。

至少如果我计划使用那些编译器和选项的代码。

1) [conv.lval],C ++ 11和C ++ 14中的§4.1/ 1。

答案 2 :(得分:8)

这是来自N3797文件的第4.7段“整体转换”,这是C ++ 14标准的最新工作草案:

  

如果目标类型是无符号的,则结果值最小   无符号整数与源整数一致(模2 n 其中 n 是   用于表示无符号类型的位数)。 [注意:在   两个补码表示,这种转换是概念性的   位模式没有变化(如果没有截断)。    - 后注]

首先,世界上所有计算机都使用二进制补码表示。所以[1]是要走的路(除非你将C ++移植到IBM 7090)。

答案 3 :(得分:7)

[3]在C和C ++中都是正确的(从C ++ 14开始,但之前没有);在这种情况下,无需使用memcpy。 (也就是说,没有理由使用memcpy,因为它有效地传达了您的意图,显然是安全的,并且没有开销。 )

C, 6.5表达式

  

7 - 对象的存储值只能由具有其中一个的左值表达式访问   以下类型:[...]

     
      
  • 对应于有效类型的有符号或无符号类型的类型   对象,[...]
  •   

C ++, [basic.lval]

  

10 - 如果程序试图通过除了其中一个之外的glvalue访问对象的存储值   以下类型的行为未定义:[...]

     
      
  • 与对象的动态类型[...]
  • 对应的有符号或无符号类型   

正如您所看到的,两个标准中的措辞非常相似,因此可以依赖这两种语言。