/* [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]
怎么样?它能有效地达到我想要的目的吗?
答案 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访问对象的存储值 以下类型的行为未定义:[...]
- 与对象的动态类型[...]
对应的有符号或无符号类型
正如您所看到的,两个标准中的措辞非常相似,因此可以依赖这两种语言。