如何通过64位右移是标识而不是幂零(返回0)?

时间:2012-07-06 09:57:42

标签: c gcc x86-64

这段代码不言而喻,但我不能发布问题,除非我在这里说些什么!

#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

1 个答案:

答案 0 :(得分:3)

ISO C99标准有一个处理bitshift运算符的小片段:

  

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

     

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

换句话说,一旦你开始尝试将64位(在你的实现中)unsigned long移位64位或更多,所有的赌注都会被关闭。它可以返回0,原始值或42.它甚至可以格式化您的硬盘驱动器并将色情邮件发送给您的老板(虽然我从未见过实现这个)。

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

在这种情况下,由于64 & 0x3f等于零,因此它实际上是无操作。

在任何情况下,为什么它的行为方式都是假设。未定义的行为是您真正应该避免的。如果确实希望行为按预期工作,请更改:

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