也许值得解释一下产生数字 18446744073692774400 的原因。从技术上讲,您编写的表达式触发了“未定义的行为”,因此编译器可能会产生任何结果;但是,假设int
是 32 位类型,现在几乎总是这样,如果你写,你会得到相同的“错误”答案
uint64_t x = (int) (255u*256u*256u*256u);
并且该表达式不会触发未定义的行为。(从unsigned int
to的转换int
涉及实现定义的行为,但由于多年来没有人生产过补码或符号和大小 CPU,因此您可能遇到的所有实现都以完全相同的方式定义它。)我已经写了以 C 风格进行转换,因为我在这里所说的一切都同样适用于 C 和 C++。
首先,让我们看一下乘法。我用十六进制写右手边,因为这样更容易看到发生了什么。
255u * 256u = 0x0000FF00u
255u * 256u * 256u = 0x00FF0000u
255u * 256u * 256u * 256u = 0xFF000000u (= 4278190080)
最后一个结果 ,0xFF000000u
具有 32 位数字集的最高位。因此,将该值转换为带符号的32 位类型会导致它变为负数,就好像从其中减去 2 32一样(这是我上面提到的实现定义的操作)。
(int) (255u*256u*256u*256u) = 0xFF000000 = -16777216
我在那里写了十六进制数字,无u
后缀,以强调当您将其转换为有符号类型时,该值的位模式不会改变;它只是被重新解释。
现在,当您将 -16777216 分配给uint64_t
变量时,它会通过添加 2 64反向转换为无符号的 as-if 。(与无符号到有符号的转换不同,此语义是由标准规定的。)这确实改变了位模式,将数字的所有高 32 位设置为 1 而不是您预期的 0:
(uint64_t) (int) (255u*256u*256u*256u) = 0xFFFFFFFFFF000000u
如果你用0xFFFFFFFFFF000000
十进制写,你会得到 18446744073692774400。
作为最后一条建议,每当你从 C 或 C++ 中得到一个“不可能的”整数时,试着用十六进制打印出来;以这种方式更容易看到二进制补码固定宽度算术的奇怪之处。