32

在第 2 章,关于按位运算符的部分(第 2.9 节)中,我无法理解其中一种示例方法的工作原理。

这是提供的方法:

unsigned int getbits(unsigned int x, int p, int n) {
    return (x >> (p + 1 - n)) & ~(~0 << n);
}

这个想法是,对于给定的数字x,它将返回从位置p开始的n位,从右侧开始计数(最右边的位是位置 0)。给定以下方法:main()

int main(void) {
    int x = 0xF994, p = 4, n = 3;
    int z = getbits(x, p, n);
    printf("getbits(%u (%x), %d, %d) = %u (%X)\n", x, x, p, n, z, z);

    return 0;
}

输出是:

getbits(63892 (f994), 4, 3) = 5 (5)

我得到了其中的一部分,但在“大图”方面遇到了麻烦,主要是因为我不理解的位(不是双关语)。

我特别有问题的部分是补充部分:~(~0 << n). 我想我得到了第一部分,处理x;这是我正在努力解决的部分(然后是掩码)——以及它们是如何结合在一起来实际检索这些位的。(我已经验证了它正在做的事情,包括代码和使用 calc.exe 检查我的结果——感谢上帝,它有一个二进制视图!)

有什么帮助吗?

4

6 回答 6

42

让我们在示例中使用 16 位。在这种情况下,~0等于

1111111111111111

当我们左移此n位(在您的情况下为 3)时,我们得到:

1111111111111000

因为1左边的 s 被丢弃,0右边的 s 被输入。然后重新补充它给出:

0000000000000111

n所以这只是在数字的最不重要部分获得 1 位的聪明方法。

您描述的“x 位”已将给定数字 ( f994 = 1111 1001 1001 0100) 移动得足够远,因此最低有效 3 位是您想要的。在此示例中,您请求的输入位在那里,所有其他输入位都被标记.,因为它们对最终结果并不重要:

ff94             ...........101..  # original number
>> p+1-n     [2] .............101  # shift desired bits to right
& ~(~0 << n) [7] 0000000000000101  # clear all the other (left) bits

如您所见,您现在在最右边的位位置拥有相关位。

于 2008-10-13T13:59:08.803 回答
13

我想说最好的办法是手工解决问题,这样你就会明白它是如何工作的。

这是我使用 8 位无符号整数所做的。

  1. 我们的数字是 75,我们想要从位置 6 开始的 4 位。函数的调用是 getbits(75,6,4);

  2. 二进制的 75 是 0100 1011

  3. 所以我们创建了一个 4 位长的掩码,从最低位开始,就这样完成了。

~0 = 1111 1111
<<4 = 1111 0000
~ = 0000 1111

好的,我们得到了我们的面具。

  1. 现在,我们将我们想要的位从数字中推入最低位,因此我们将二进制 75 移动 6+1-4=3。

0100 1011 >>3 0000 1001

现在我们有了一个掩码,其中包含正确的低位位数和我们想要的低位原始位数中的位。

  1. 所以我们和他们
  0000 1001
& 0000 1111 ============= 0000 1001

所以答案是十进制的 9。

注意:高阶半字节恰好全为零,在这种情况下使掩码变得多余,但它可能是任何东西,具体取决于我们开始的数字的值。

于 2008-10-13T14:57:26.313 回答
6

~(~0 << n)创建一个将n打开最右边的位的掩码。

0
   0000000000000000
~0
   1111111111111111
~0 << 4
   1111111111110000
~(~0 << 4)
   0000000000001111

将结果与其他内容相加将返回这些n位中的内容。

编辑:我想指出我一直在使用的这个程序员的计算器:AnalogX PCalc

于 2008-10-13T14:02:19.687 回答
4

还没有人提到它,但在 ANSI C~0 << n中会导致未定义的行为。

这是因为~0是一个负数并且左移负数是未定义的。

参考:C11 6.5.7/4(早期版本有类似文字)

的结果E1 << E2E1左移的E2位位置;空出的位用零填充。[...] 如果 E1 具有有符号类型和非负值,并且E1×2E2在结果类型中是可表示的,那么这就是结果值;否则,行为未定义。

在 K&R C 中,此代码将依赖于 K&R 开发的特定类系统,1在执行有符号数的左移时天真地向左移位位(并且此代码也依赖于 2 的补码表示),但其他一些系统不要共享这些属性,因此 C 标准化过程没有定义这种行为。

所以这个例子真的只是作为一个历史好奇而有趣,它不应该在 1989 年以来的任何实际代码中使用(如果不是更早的话)。

于 2016-05-11T13:26:39.137 回答
2

使用示例:int x = 0xF994,p = 4,n = 3;int z = getbits(x, p, n);

并专注于这组操作~(~0 << n)

对于任何位集(10010011 等),您想要生成一个“掩码”,它只提取您想要查看的位。所以10010011或0x03,我对xxxxx011感兴趣。将提取该集合的掩码是什么?00000111 现在我想要 sizeof int 独立,我会让机器完成工作,即从 0 开始对于字节机器它是 0x00 对于字机器它是 0x0000 等。64 位机器将代表 64 位或 0x0000000000000000

现在申请“ not" (~0) 并得到 11111111
右移 (<<) n 得到 11111000
和“not”得到 00000111

所以 10010011 & 00000111 = 00000011
你还记得布尔运算是如何工作的吗?

于 2008-10-13T14:00:48.970 回答
-2

InANSI C ~0 >> n导致未定义的行为

// 关于左移导致问题的帖子是错误的。

无符号字符 m,l;

m = ~0 >> 4; 正在产生 255 并且它等于 ~0 但是,

米=〜0;l = m >> 4; 正在产生正确的值 15 与:

m = 255 >> 4;

左移负数没有~0 <<问题

于 2018-02-23T00:36:49.460 回答