0

该程序输出的每一行都等于 2 ^ i- 2,除了最后一行,等于 2 ^ 64 - 1。这是为什么呢?

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

int main(void) {
    unsigned long long ONE = 1;
    unsigned long long i;
    for (i = 1; i <= 64; i++) {
        printf("%"PRIu64"\n", (ONE << i) - 2);
    }

    return EXIT_SUCCESS;
}

输出:

0
2
6
14
30
62
126
254
510
1022
2046
...
4611686018427387902
9223372036854775806
18446744073709551615
4

4 回答 4

5

您将 64 左移到 64 位类型(unsigned long long在您的机器上),这是未定义的行为。

顺便说一句,unsigned long long ONE = 1;编码风格不好,你可以简单地使用1ULL.

C11 §6.5.7 移位运算符

对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。

于 2013-08-30T01:17:39.377 回答
4

假设ULL您的类型是 64 位宽,您将进入未定义的行为领域。根据C11 6.5.7 Bitwise shift operators

如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。

可能发生的情况是移位值以 64 为模减少,并且64 % 64为零。因此,它只是评估ONE - 2哪个包装为. 但是,老实说,它可能只是从空气中提取结果,因为 UB 没有施加真正的限制:-)264-1

于 2013-08-30T01:19:52.713 回答
1

1ULL<<64 未定义,而移位 less 已定义。

请参阅在 x86 机器上移动超过 32 位的 uint64_t 整数未定义行为吗?有关语言标准的更多详细信息。

于 2013-08-30T01:21:45.890 回答
0

您达到了 64 位(long long)的位限制。显然 2^63 左移会导致翻转,导致 2^0 = 1。

1 - 2 = -1,当被视为无符号时(假设 long long)为 2^64-1。

这个结果很有趣,因为左移的非翻转实现将导致 0,而不是 1,在减法之后会导致 2^64-2。

于 2013-08-30T01:22:45.960 回答