C++ 中奇怪的有符号/无符号整数除法

Poo*_*oSH 6 c++

int main(int argc, char *argv[]) {
    int i = -100;
    unsigned u = 10;

    cout << "i = " << i << endl;

    i *= u;
    cout << "i*10 = " << i << endl;

    i /= u;
    cout << "i = " << i << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果输出不是这样的话,我就不会在这里问这个问题:

i = -100
i*10 = -1000
i = 429496629
Run Code Online (Sandbox Code Playgroud)
  • 在乘法的情况下,无符号会隐式转换为有符号。到目前为止,一切都很好。
  • 然而,在除法的情况下,有符号会隐式转换为无符号,执行无符号除法;然后转换回签名。

有谁知道为什么会发生这种情况?因为这对我来说没有任何意义。

PS 预期的行为是通过显式类型转换实现的:i /= (int) u;

dbu*_*ush 17

在乘法的情况下,无符号会隐式转换为有符号。

那是错误的。当intandunsigned int用作运算*符或/运算符的操作数时,int参数将转换为unsigned int.

C++17 标准第 8p11 节对此进行了详细说明:

许多期望算术或枚举类型操作数的二元运算符会以类似的方式导致转换并产生结果类型。目的是产生一个通用类型,这也是结果的类型。这种模式称为通常的算术转换,其定义如下:

  • (11.1) 如果任一操作数是作用域枚举类型 (10.2),则不执行任何转换;如果另一个操作数没有相同的类型,则表达式的格式错误。
  • (11.2) 如果其中一个操作数的类型为 long double,则另一个操作数应转换为 long double。
  • (11.3) 否则,如果任一操作数为 double,则另一个操作数应转换为 double。
  • (11.4) 否则,如果其中一个操作数是浮点型,则另一个操作数应转换为浮点型。
  • (11.5) 否则,将对两个操作数执行积分提升 (7.6)。那么以下规则应应用于提升的操作数:
    • (11.5.1) 如果两个操作数具有相同类型,则不需要进一步转换。
    • (11.5.2) 否则,如果两个操作数都具有有符号整数类型或都具有无符号整数类型,则具有较小整数转换等级的类型的操作数应转换为具有较大等级的操作数的类型。
    • (11.5.3)否则,如果无符号整数类型的操作数的等级大于或等于另一个操作数类型的等级,则有符号整数类型的操作数应转换为无符号整数类型的操作数类型。
    • (11.5.4) 否则,如果有符号整型操作数的类型可以表示无符号整型操作数类型的所有值,则无符号整型操作数应转换为无符号整型操作数的类型有符号整数类型。
    • (11.5.5) 否则,两个操作数都应转换为与有符号整数类型操作数的类型相对应的无符号整数类型。

上面粗体部分适用于此。

所以在这个表达式中:

i *= u;
Run Code Online (Sandbox Code Playgroud)

这相当于:

i = i * u;
Run Code Online (Sandbox Code Playgroud)

值为i-100 的值将转换为unsigned值(假设为 32 位int)为 4294967196。乘法会导致回绕,结果为 4294966296。然后,int当分配回 时,该值将转换为i。此转换是实现定义的,尽管大多数实现将简单地重新解释表示,int这将导致值 -1000。

那么在这个表达式中:

i /= u;
Run Code Online (Sandbox Code Playgroud)

与此相同:

i = i / u;
Run Code Online (Sandbox Code Playgroud)

和之前一样的转换,所以int值-1000被转换为unsigned值4294966296。除法的结果是429496629。这个值在 an 的范围内,所以在转换为之前分配int时它没有变化inti