有符号和无符号之间的减法,然后是除法

Subtraction between signed and unsigned followed by division

下面的结果让我很困惑:

int i1 = 20-80u;    // -60
int i2 = 20-80;     // -60
int i3 =(20-80u)/2; // 2147483618
int i4 =(20-80)/2;  // -30
int i5 =i1/2;       // -30
  1. i3 似乎计算为 (20u-80u)/2,而不是 (20-80u)/2
  2. 据说i3i5相同。

IIRC,signed 和 unsigned int 之间的算术运算将产生一个 unsigned 结果。

因此,20 - 80u 产生等同于 -60 的无符号结果:如果 unsigned int 是 32 位类型,则结果为 4294967236。

顺便说一句,将其分配给 i1 会产生 实现定义的 结果,因为数字太大而不适合。获得 -60 是典型的,但不能保证。

int i1 = 20-80u;    // -60

这有鬼!操作数不同,需要转换。两个操作数都转换为通用类型(在本例中为 unsigned int)。结果将是一个很大的 unsigned int 值(如果我的计算正确,则比 UINT_MAX + 1 小 60)将在存储到 i1 之前转换为 int。由于该值超出 int 的范围,结果将由实现定义,可能是陷阱表示,因此当您尝试使用它时可能会导致未定义的行为。但是,在您的情况下,它恰好转换为 -60.


int i3 =(20-80u)/2; // 2147483618

从第一个例子继续,我的猜测是 20-80u 的结果会比 UINT_MAX + 1 少 60。如果 UINT_MAX 是 4294967295(UINT_MAX 的通用值),则意味着 20-80u4294967236... 而 4294967236 / 2 是 2147483618。


至于i2等人,应该不会有什么意外。它们遵循传统的数学计算,没有任何转换、截断、溢出或其他实现定义的行为。

二元算术运算符将对它们的操作数执行 usual arithmetic conversions 以使它们成为通用类型。

i1i3i5 的情况下,常见类型将是 unsigned int 因此结果也将是无符号整数。无符号数将通过模运算换行,因此减去稍大的无符号值将导致接近 unsigned int max 的数字不能用 int 表示。

所以在 i1 的情况下,我们最终得到一个实现定义的转换,因为该值无法表示。在 i3 除以 2 的情况下,将无符号值带回到 int 的范围内,因此我们在转换后得到一个大的有符号 int 值。

C++标准草案的相关部分如下。第 5.7 [expr.add]:

The additive operators + and - group left-to-right. The usual arithmetic conversions are performed for operands of arithmetic or enumeration type.

通常的算术转换在 5 节中介绍,它说:

Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:

[...]

  • Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type shall be converted to the type of the operand with unsigned integer type.

并且对于从不能表示为带符号类型的值的转换,部分 4.7 [conv.integral]:

If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.

并且对于无符号整数,服从模算术部分 3.9.1 [basic.fundamental]:

Unsigned integers shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer.48