【问题标题】:k&r exercise confusion with bit-operationsk&r 与位操作混淆
【发布时间】:2010-01-16 03:24:52
【问题描述】:

练习是: 编写一个函数 setbits(x,p,n,y) 返回 x ,其中从位置 p 开始的 n 位设置为 y 的最右边 n 位,其他位保持不变。

我的解决方案是:

#include <stdio.h>

unsigned setbits(unsigned, int, int, unsigned);

int main(void)
{
    printf("%u\n", setbits(256, 4, 2, 255));
    return 0;
}

unsigned setbits(unsigned x, int p, int n, unsigned y)
{
    return (x >> (p + 1 - n)) | (1 << (n & y));
}

这可能是不正确的,但我在正确的道路上吗?如果没有,我做错了什么?我不确定为什么我不能完全理解这一点,但我花了大约一个小时试图想出这个。

谢谢。

【问题讨论】:

  • 关于同一个 K&R 问题的问题 - 那里的解释可能会有所帮助。但不完全相同的问题; svr这里已经努力提供代码了。
  • @Jonathan,我同意。这就是我没有投票关闭它的原因。

标签: c bit-manipulation kr-c


【解决方案1】:

这是你的算法:

  1. 如果 n 为 0,则返回 x。
  2. 取 1,将其左移 n 次,然后减 1。将此称为 mask
  3. 左移掩码 p 次调用此mask2
  4. And x 与 mask2 的倒数。 And y 带掩码,左移 p 次。
  5. Or 这两个操作的结果,并返回该值。

【讨论】:

  • 像这样: unsigned setbits(unsigned x, int p, int n, unsigned y) { int mask = (1
  • 看起来比我之前得到的答案更好:) unsigned setbits(unsigned x, int p, int n, unsigned y) { int mask = (1
  • 你对减法/移位是正确的,所以我会解决这个问题。但是二进制中的“位置”从右边开始,从 0 开始,向左递增。
  • 我将“从 p 开始的 n 位”表示“从位位置 p 开始,并‘向右’移动 n-1 位”。看起来stackoverflow.com/questions/1415854/kr-c-exercise-help 同意我的看法。
【解决方案2】:

我认为答案是对第 2.9 节中的 getbits 示例的稍微修改的应用程序。

让我们分解如下:

Let bitstring x be 1 0 1 1 0 0
Let bitstring y be 1 0 1 1 1 1

positions -------->5 4 3 2 1 0

设置p = 4 and n =3 为我们提供了来自x 的位串0 1 1。它从 4 开始,以 2 结束,跨越 3 个元素。

我们要做的是将0 1 1替换为1 1 1(位串y的最后三个元素)。

让我们暂时忘记左移/右移,并将问题可视化如下:

我们需要从位串 y 中获取最后三位数字,即1 1 1

1 1 1 直接放在位串x 的位置4 3 and 2 下。

0 1 1 替换为1 1 1,同时保持其余位不变...

现在让我们更详细一点...

我的第一句话是:

We need to grab the last three digits from bitstring y which is 1 1 1

从位串中分离位的方法是首先从全为 0 的位串开始。 我们最终得到0 0 0 0 0 0

0 具有这种令人难以置信的特性,其中按位 '&' 与另一个数字会得到全 0,而按位 '|' 与另一个数字会返回另一个数字。

0 本身在这里没有用......但它告诉我们,如果我们 '|' y 的最后三位数字为“0”,我们将得到 1 1 1。y 中的其他位在这里并不真正关心我们,因此我们需要找到一种方法将这些数字归零,同时保持最后三位数字完好无损。本质上,我们需要号码0 0 0 1 1 1

让我们看一下所需的一系列转换:

Start with  ->  0 0 0 0 0 0
apply ~0    ->  1 1 1 1 1 1
lshift by 3 ->  1 1 1 0 0 0 
apply ~     ->  0 0 0 1 1 1
& with y    ->  0 0 0 1 1 1 & 1 0 1 1 1 1 -> 0 0 0 1 1 1

这样我们就可以将最后三位数字用于设置目的......

我的第二句话是:

将 1 1 1 直接放在位串 x 的位置 4 3 和 2 下。

可以从第 2.9 节中的 getbits 示例中找到执行此操作的提示。我们对位置 4,3 和 2 的了解可以从值 p = 4 and n =3 中找到。 p 是位置,n 是位集的长度。结果p+1-n 为我们提供了位集相对于最右边位的偏移量。在这个特定的例子中p+1-n = 4 +1-3 = 2

所以..如果我们在字符串 0 0 0 1 1 1 上左移 2,我们最终得到 0 1 1 1 0 0。如果将此字符串放在 x 下,您会注意到 1 1 1 与 x 的位置 4 3 and 2 对齐。

我想我终于有所收获了……我最后的声明是……

将 0 1 1 替换为 1 1 1,同时保持其余位不变...

现在让我们回顾一下我们的字符串:

x           ->   1 0 1 1 0 0
isolated y  ->   0 1 1 1 0 0

对这两个值进行按位或运算可以得到我们在这种情况下所需要的:

1 1 1 1 0 0 

但是如果我们有1 0 1而不是1 1 1,这将失败......所以如果我们需要再挖掘一点来获得我们的“银弹”......

让我们再看一遍上面的两个字符串...

x -> bit by bit...1(stays) 0(changes) 1(changes) 1(changes) 0(stays) 0(stays)

所以理想情况下..我们需要位串1 x x x 0 0,其中 x 将与 1 交换。 这是直觉的飞跃,它将帮助我们..

Bitwise complement of isolated y -> 1 0 0 0 1 1
& this with x gives us           -> 1 0 0 0 0 0
| this with isolated y           -> 1 1 1 1 0 0 (TADA!)

希望这篇长文能帮助人们合理化和解决此类位掩码问题...

谢谢

【讨论】:

  • 谢谢,这很有帮助,因为其他答案都没有真正解释发生了什么。
【解决方案3】:

请注意,~0 &lt;&lt; i 为您提供了一个数字,其中最低有效位 i 设置为 0,其余位设置为 1。同样,~(~0 &lt;&lt; i) 为您提供了一个数字,其中最低有效位 i 设置为 1,其余为 0

现在,解决您的问题:

  1. 首先,您需要一个数字,该数字除了从位置p 开始的n 位之外的所有位都设置为x 的位。为此,您需要一个包含1 的掩码,除了从p 位置开始的n 位之外的所有位置:
    1. 此掩码设置了最高(最高有效)位,从位置 p+1 处的位开始。
    2. 此掩码还设置了最低有效的p+1-n 位。
  2. 一旦你有了上面的掩码,这个掩码的&amp;x 会给你在步骤1中你想要的号码。
  3. 现在,您需要一个具有y 的最低有效n 位的数字,左移p+1-n 位。
    1. 您可以轻松地制作一个只设置了最低有效n 位的掩码,并将&amp;y 一起提取y 的最低有效n 位。
    2. 然后,您可以将此数字移动p+1-n 位。
  4. 最后,您可以按位或 (|) 步骤 2 和 3.2 的结果来获得您的号码。

一清二楚? :-)

(上述方法应该与数字的大小无关,我认为这很重要。)

编辑:看看你的努力:n &amp; yn 位没有任何作用。例如,如果n 是 8,您需要 y 的最后 8 位,但 n &amp; y 只会选择 y 的第 4 位(二进制中的 8 是 1000)。所以你知道这是不对的。类似地,右移xp+1-n 次可以得到一个数字,其中最高有效位p+1-n 设置为零,其余位由x 的最高有效位组成。这也不是你想要的。

【讨论】:

    猜你喜欢
    • 2021-10-07
    • 2018-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多