use*_*570 3 c++ signed integer-overflow undefined-behavior c++11
我试图理解这句话的含义:
(int)(unsigned)-1 == -1;
Run Code Online (Sandbox Code Playgroud)
据我目前的理解,会发生以下事情:
-1
是一个有符号的 int
并且被转换为无符号的 int
。其结果是,由于环绕行为,我们获得了该类型可以表示的最大值unsigned
。
接下来,unsigned
我们在步骤 1 中获得的这种类型最大值现在被转换为signed int
。但请注意,该最大值是一个unsigned type
。所以这超出了的范围signed type
。并且由于有符号整数溢出是未定义的行为,因此程序将导致未定义的行为。
我的问题是:
PS:我知道如果它是未定义的行为(而不是定义的实现),那么我们不能依赖程序的输出。所以我们不能说我们是否总是会得到true
或false
。
总而言之unsigned int
,这部分是合法的。
从 C++20 开始,超出范围的转换为int
合法的,并且之前是实现定义的(但无论如何在实践中工作正常)。这里没有UB。
这两个强制转换相互抵消(同样,在 C++20 中保证,之前是实现定义的,但无论如何在实践中都是有效的)。
有符号溢出通常是 UB,是的,但这仅适用于由计算引起的溢出。转换引起的溢出是不同的。
如果目标类型有符号,并且源整数可以用目标类型表示,则该值不会更改。否则结果是:
(C++20 之前)实现定义
(C++20 起) 目标类型的唯一值等于源值模,其中是用于表示目标类型的位数。
2n
n
(请注意,这与有符号整数算术溢出不同,后者是未定义的)。
有关转换如何运作的更多细节。
假设int
占用unsigned int
N 位。
int
两者都可以表示并且unsigned int
不会因转换而改变的值。所有其他值均增加或减少 2 N以适应该范围。
这很方便,不会改变值的二进制表示形式。
例如int
-1
对应(最大值),两者都用二进制表示。同样,(最小值)对应于(最大值+1)。unsigned int
2N-1
unsigned int
11...11
int
-2N-1
int
unsigned int
2N-1
int
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)