C++代码中带有signed和unsigned之间的隐式转换,安全吗?

13

根据有关有符号和无符号整数类型之间隐式转换的规则(在此讨论),当将一个 unsigned int 与一个 int 相加时,有符号的 int 首先被转换为 unsigned int

例如,请考虑以下最小化程序:

#include <iostream>

int main()
{
   unsigned int n = 2;
   int x = -1;

   std::cout << n + x << std::endl;

   return 0;
}

程序的输出仍然是1,正如预期的那样:x首先被转换为unsigned int,与n相加导致整数溢出,得出了“正确”的答案。

在像上面代码这样的情况下,如果我确定n + x是正数,那么我可以假设unsigned int nint x的总和会给出预期的值吗?


1
你是否启用了所有警告进行编译? - JGroven
2
@JGroven,那会对答案产生什么影响? - Fureeish
2
如果编译器警告你正在做的事情,那么你可能不应该这样做。它是一个“警告”,目的是提醒你注意,以防万一。我遇到过很多情况,在生产代码中出现了“警告:未使用的变量X”,但实际上这些代码是正确的(也许仅仅因为声明本身就有影响或者编译器是错误的)。 - scohe001
2
这将是实现定义。标准规定有符号和无符号的位模式将是相同的,但不保证采用二进制补码。因此,如果这样做是否有效将是实现定义的。我相信 std::int8/16/32_t 保证采用二进制补码,因此它们始终有效。 - Galik
1
@Galik:在我看来,表示方式并不重要。标准是清晰的。结果将是模2^n同余的一个相合数。你提到的标准中哪一部分是矛盾的呢? - geza
显示剩余19条评论
2个回答

5
在像前面那样的代码中,如果我确定 n+x 是正数,那么我能假设无符号整数 n 和有符号整数 x 的和会得到期望值吗?
可以。首先,有符号值通过模运算转换为无符号值:
如果目标类型是无符号的,则结果值是源整数对 2n 取模后最小的无符号整数。
然后使用模运算相加两个无符号值:
无符号整数应遵守算术模 2n 的规则,其中 n 是该特定大小的整数的值表示中使用的位数。
这意味着您将获得预期的答案。即使在数学意义上结果为负数,C++ 中的结果也是模等于负数的数字。
请注意,此处假定您正在添加两个相同大小的整数。

1
我认为你可以确定这不是实现定义,尽管在使用非二进制补码表示负值的系统中,该语句需要对标准进行一些解释。首先,让我们说明一些明确的事情:无符号整数不会溢出,但取模于2^nrOfBits-value(参见此在线C ++标准草案):
“6.7.1基本类型 (7)无符号整数应遵守算术模2n的规律,其中n是该特定大小的整数值表示中的位数。”
因此,只需将负值nv正确转换为无符号整数位模式nv(conv),使得x + nv(conv)始终与x - nv相同。对于使用二进制补码的系统,情况很明确,因为二进制补码实际上是设计用于立即处理此算术的。
对于使用其他表示负值的系统,我们必须仔细阅读标准:

7.8 整数转换

(2) 如果目标类型为无符号类型,则结果值是与源整数同余的最小无符号整数(模2n,其中n是用于表示无符号类型的位数)。[注意:在二进制补码表示中,此转换是概念性的,并且在没有截断的情况下不会更改位模式。—注释]

正如脚注明确指出的那样,在二进制补码表示中,位模式不会更改,我们可以假设在非2s补码系统中会发生真实的转换,使得x + nv(conv) == x - nv

因此,根据7.8(2),我认为您的假设是有效的。


1
算术是基于数值而非表示定义的。无论系统是否使用二进制补码都没有任何影响。2 + -1 的结果必须为 1 - M.M
@M.M:是的,这就是为什么结果不依赖于实现的原因,尽管在其他地方有人争辩说它可能只适用于二进制补码的情况。 - Stephan Lechner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接