需要帮助理解K& R C第2章中的“getbits()”方法

时间:2008-10-13 13:46:05

标签: c operators bit-manipulation bit-shift complement

在第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检查我的结果 - 感谢上帝它有一个二进制视图!)

任何帮助?

6 个答案:

答案 0 :(得分:37)

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

1111111111111111

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

1111111111111000

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

0000000000000111

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

您描述的“x位”已将给定数字(f994)向右移动得足够远,以便最不重要的3位是您想要的位数。在此示例中,您请求的位被“。”包围。字符。

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

你有你的位。 Ta da !!

答案 1 :(得分:11)

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

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

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

  2. 75 in binary是0100 1011

  3. 因此,我们创建一个4位长的掩码,从最低位开始,这样做。

  4. ~0 = 1111 1111
    &lt;&lt;&lt; 4 = 1111 0000
    〜= 0000 1111

    好的,我们戴上了面具。

    1. 现在,我们将我们想要的数字从数字中推出到最低位 我们将二进制75移位6 + 1-4 = 3.
    2. 0100 1011&gt;&gt; 3 0000 1001

      现在我们有一个低位正确位数的掩码和我们想要的低位原始数字的位。

      1. 所以我们&amp;他们
      2.   0000 1001 
        & 0000 1111 ============ 0000 1001

        所以答案是小数9。

        注意:高阶半字节恰好全部为零,在这种情况下使屏蔽多余,但它可能是任何取决于我们开始的数字的值。

答案 2 :(得分:6)

~(~0 << n)会创建一个屏幕,该屏幕最多会打开n个位。

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

使用其他内容对结果进行AND运算将返回n位中的内容。

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

答案 3 :(得分:3)

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

这是因为~0是负数,左移负数是未定义的。

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

  

E1 << E2的结果是E1左移E2位位置;腾出的位用零填充。 [...]如果E1有签名   类型和非负值,E1×2 E2 在结果类型中可表示,然后是结果值;否则,行为未定义。

在K&amp; RC中,这段代码依赖于K&amp; R开发的特定类别的系统,在执行有符号数字的左移时,从左侧移开1位(此代码也是依赖于2的补码表示,但是其他一些系统不共享这些属性,所以C标准化过程没有定义这种行为。

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

答案 4 :(得分:2)

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

并专注于这组操作    〜(~0 <&lt; n)

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

现在申请“不”(~0)并获得11111111
向右移动(&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&quot; 和“不”那并获得00000111

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

答案 5 :(得分:-2)

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

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

unsigned char m,l;

m = ~0&gt;&gt; 4;产生255并且它等于〜0但

m = ~0; l = m&gt;&gt; 4;产生的正确值15与:

相同

m = 255&gt;&gt; 4;

左移{@ 1}}无任何问题