(int)(unsigned)-1 == -1 是未定义行为

use*_*570 3 c++ signed integer-overflow undefined-behavior c++11

我试图理解这句话的含义:

(int)(unsigned)-1 == -1;
Run Code Online (Sandbox Code Playgroud)

据我目前的理解,会发生以下事情:

  1. -1是一个有符号的 int并且被转换为无符号的 int。其结果是,由于环绕行为,我们获得了该类型可以表示的最大值unsigned

  2. 接下来,unsigned我们在步骤 1 中获得的这种类型最大值现在被转换为signed int。但请注意,该最大值是一个unsigned type。所以这超出了的范围signed type。并且由于有符号整数溢出是未定义的行为,因此程序将导致未定义的行为

我的问题是:

  1. 我上面的解释正确吗?如果不是,那么实际发生了什么。
  2. 这是我怀疑的未定义行为还是实现定义的行为。

PS:我知道如果它是未定义的行为(而不是定义的实现),那么我们不能依赖程序的输出。所以我们不能说我们是否总是会得到truefalse

Hol*_*Cat 8

总而言之unsigned int,这部分是合法的。

从 C++20 开始,超出范围的转换为int合法的,并且之前是实现定义的(但无论如何在实践中工作正常)。这里没有UB。

这两个强制转换相互抵消(同样,在 C++20 中保证,之前是实现定义的,但无论如何在实践中都是有效的)。

有符号溢出通常是 UB,是的,但这仅适用于由计算引起的溢出。转换引起的溢出是不同的。

参考参数

如果目标类型有符号,并且源整数可以用目标类型表示,则该值不会更改。否则结果是:

(C++20 之前)实现定义

(C++20 起) 目标类型的唯一值等于源值模,其中是用于表示目标类型的位数。2nn

(请注意,这与有符号整数算术溢出不同,后者是未定义的)。


有关转换如何运作的更多细节。

假设int占用unsigned intN 位。

int两者都可以表示并且unsigned int不会因转换而改变的值。所有其他值均增加或减少 2 N以适应该范围。

这很方便,不会改变值的二进制表示形式。

例如int -1对应(最大值),两者都用二进制表示。同样,(最小值)对应于(最大值+1)。unsigned int 2N-1unsigned int11...11int -2N-1intunsigned int 2N-1int

int:   [-2^(n-1)] ... [-1] [0] [1] ... [2^(n-1)-1]
            |           |   |   |           |
            |           '---|---|-----------|-----------------------.
            |               |   |           |                       |
            '---------------|---|-----------|----------.            |
                            |   |           |          |            |
                            V   V           V          V            V
unsigned int:              [0] [1] ... [2^(n-1)-1] [2^(n-1)] ... [2^n-1]
Run Code Online (Sandbox Code Playgroud)