需要帮助理解 K&R C 第 2 章中的“getbits()”方法
Posted
技术标签:
【中文标题】需要帮助理解 K&R C 第 2 章中的“getbits()”方法【英文标题】:Need help understanding "getbits()" method in Chapter 2 of K&R C 【发布时间】:2010-09-16 21:23:59 【问题描述】:在第 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 检查我的结果——感谢上帝,它有一个二进制视图!)
有什么帮助吗?
【问题讨论】:
【参考方案1】:让我们在示例中使用 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
如您所见,您现在在最右边的位位置拥有相关位。
【讨论】:
我本来打算建议右移以保存指令,但我想,~(~0 << n)
可以优雅地让您忽略正在使用的字长...
我有些不明白,我哪里错了?整数a = 0;所以 printf("%d", a) 给出 0。现在,a = ~a 所以 printf("%d", a) 给出 -1,为什么?
@Anatoly:这是因为~
将所有位反转为 1,并且在二进制补码编码中为 -1。
很好的答案。我终于明白这个例子了! :)【参考方案2】:
使用示例: int x = 0xF994,p = 4,n = 3; int z = getbits(x, p, n);
并专注于这组操作 ~(~0
对于任何位集(10010011 等),您想要生成一个“掩码”,它只提取您想要查看的位。所以10010011或0x03,我对xxxxx011感兴趣。将提取该集合的掩码是什么? 00000111 现在我想独立于 sizeof int,我会让机器完成工作,即从 0 开始对于字节机器它是 0x00 对于字机器它是 0x0000 等。64 位机器将由 64 位或 0x0000000000000000 表示 现在应用 "not" (~0) 并得到 11111111 将 ( 和“不”那个并得到 00000111 所以 10010011 & 00000111 = 00000011 你还记得布尔运算的工作原理吗?
【讨论】:
@jim:嘿,不要敲你帖子的准确性。内容方面的工作比其他两个要多,最好使用代码块并对齐更改。这些帖子使用 wiki 标记,并且一个 tute 页面链接到“?”答案框上方。我必须阅读它两次才能检查它。【参考方案3】:~(~0 << n)
创建一个掩码,该掩码将打开最右边的n
位。
0
0000000000000000
~0
1111111111111111
~0 << 4
1111111111110000
~(~0 << 4)
0000000000001111
将结果与其他内容进行与运算将返回 n
位中的内容。
编辑:我想指出这个我一直在使用的程序员计算器:AnalogX PCalc。
【讨论】:
【参考方案4】:我会说最好的办法是手工解决问题,这样你就会明白它是如何工作的。
这是我使用 8 位无符号整数所做的。
我们的数字是 75,我们想要从位置 6 开始的 4 位。 该函数的调用将是 getbits(75,6,4);
二进制的75是0100 1011
因此,我们创建了一个 4 位长的掩码,从最低位开始,这样就完成了。
~0 = 1111 1111 ~ = 0000 1111
好的,我们得到了我们的面具。
-
现在,我们将数字中想要的位推入最低位,因此
我们将二进制 75 移动 6+1-4=3。
0100 1011 >>3 0000 1001
现在我们有了一个掩码,其中包含正确的低位位数和我们想要的低位原始位数中的位数。
-
所以我们和他们
所以答案是十进制的 9。
注意: 高阶半字节恰好全为零,在这种情况下使屏蔽变得多余,但根据我们开始时的数字值,它可能是任何值。
【讨论】:
【参考方案5】:还没有人提到它,但在 ANSI C 中 ~0 << n
会导致未定义的行为。
这是因为~0
是一个负数,左移负数是未定义的。
参考:C11 6.5.7/4(早期版本有类似文字)
E1 << E2
的结果是E1
左移E2
位位置;空出的位用零填充。 [...] 如果 E1 有一个签名 类型和非负值,而E1
×2
E2
在结果类型中是可表示的,那么就是结果值;否则,行为未定义。
在 K&R C 中,此代码将依赖于 K&R 开发的特定系统类别,当执行有符号数的左移时,天真地将 1
位左移(并且此代码还依赖于 2 的补码表示),但其他一些系统不共享这些属性,因此 C 标准化过程没有定义这种行为。
所以这个例子真的只是作为一个历史好奇而有趣,它不应该在 1989 年以来的任何实际代码中使用(如果不是更早的话)。
【讨论】:
【参考方案6】:在ANSI C ~0 >> n
中导致未定义的行为
//关于左移导致问题的帖子是错误的。
无符号字符 m,l;
m = ~0 >> 4;正在产生 255 并且它等于 ~0 但是,
m = ~0; l = m >> 4;正在产生与以下相同的正确值 15:
m = 255 >> 4;
左移负数~0 <<
无论如何都没有问题
【讨论】:
以上是关于需要帮助理解 K&R C 第 2 章中的“getbits()”方法的主要内容,如果未能解决你的问题,请参考以下文章