-1

这段代码不言自明,但除非我在这里说些什么,否则我无法发布问题!

#include <stdio.h>
#include <limits.h>

void rightshift_test(int const shift_amount) {
  unsigned long a = 0xabcd1234abcd1234UL;
  printf("a: %lx, shift_amount:%d\n", a, shift_amount);
  unsigned long const b = a >> shift_amount;
  printf("b: %lx\n", b);
}

int main() {
  rightshift_test(56);
  rightshift_test(60);
  rightshift_test(61);
  rightshift_test(62);
  rightshift_test(63);
  rightshift_test(64);
  return 0;
}

它仍然不允许我发布问题。这里代码正在运行:

gcc -Wall -Wextra -Werror foo.c -o foo
./foo
a: abcd1234abcd1234, shift_amount:56
b: ab
a: abcd1234abcd1234, shift_amount:60
b: a
a: abcd1234abcd1234, shift_amount:61
b: 5
a: abcd1234abcd1234, shift_amount:62
b: 2
a: abcd1234abcd1234, shift_amount:63
b: 1
a: abcd1234abcd1234, shift_amount:64
b: abcd1234abcd1234

这里是我的编译器和我的机器的细节,以防这不是很明显。

$ gcc --version
i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)

$ echo $MACHTYPE
x86_64-apple-darwin10.0
4

1 回答 1

3

ISO C99 标准有这个处理位移运算符的小片段:

shift-expression:
       additive-expression
       shift-expression << additive-expression
       shift-expression >> additive-expression

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

换句话说,一旦您开始尝试将 64 位(在您的实现中)移动unsigned long64 位或更多位,所有的赌注都没有了。它可以返回 0、原始值或 42。它甚至可以格式化您的硬盘驱动器并将色情邮件发送给您的老板(尽管我从未见过真正做到这一点的实现)。

您可能会发现某些实现(甚至实现的底层硬件)将通过仅有效地使用相关位来优化这些指令。换句话说,对于一个 64 位的值,它很可能只使用最低有效的 7 位(有效地使用and-ing 0x3f)来为您提供 0 到 63 的操作数。

在这种情况下,因为64 & 0x3f等于 0,所以它实际上是一个空操作。

无论如何,为什么它会以现在的方式行事是假设。未定义的行为是您真正应该避免的。如果您真的希望行为按预期工作,请更改:

unsigned long const b = a >> shift_amount;

变成类似的东西:

unsigned long const b = (shift_amount < 64) ? a >> shift_amount : 0;

如这段代码所示(64 位值是我系统上的 ULL):

#include <stdio.h>
#include <limits.h>

void rightshift_test(int const shift_amount) {
    unsigned long long a = 0xabcd1234abcd1234ULL;
    printf("a: %llx, shift_amount:%d: ", a, shift_amount);
    unsigned long long const b = (shift_amount < 64) ? a >> shift_amount : 0;
    printf("b: %llx\n", b);
}

int main() {
    rightshift_test(56);
    rightshift_test(60);
    rightshift_test(61);
    rightshift_test(62);
    rightshift_test(63);
    rightshift_test(64);
    rightshift_test(99);
    return 0;
}

这个的输出是:

a: abcd1234abcd1234, shift_amount:56: b: ab
a: abcd1234abcd1234, shift_amount:60: b: a
a: abcd1234abcd1234, shift_amount:61: b: 5
a: abcd1234abcd1234, shift_amount:62: b: 2
a: abcd1234abcd1234, shift_amount:63: b: 1
a: abcd1234abcd1234, shift_amount:64: b: 0
a: abcd1234abcd1234, shift_amount:99: b: 0
于 2012-09-13T08:14:55.167 回答