【问题标题】:K&R exercise 2.6 bit minipulationK&R 练习 2.6 位操作
【发布时间】:2021-01-31 15:33:39
【问题描述】:

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

我很难弄清楚我做错了什么。

#include <stdio.h>
unsigned setbits(unsigned int x, int p, int n, unsigned y);

int main()
{
    unsigned x = 213;
    unsigned y = 121;
    int p = 4;
    int n = 4;

    x = setbits(x, p, n, y);
    printf("%u\n", x);

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

结果我在纸上得到 211,但我的代码生成 210。K&R 答案簿算法也返回 210。我不知道我在这里做错了什么。

编辑:这是答案簿中的代码:

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

重新编辑。我在将代码分成单独的行时发现了这一点。问题是最后一个 x 旁边的括号放错了:

//original
{
    return ((~(~0 << n) & y) << (p + 1 - n) | (~(~(~0 << n)) << (p + 1 - n) & x));
}

//fixed
{
    return ((~(~0 << n) & y) << (p + 1 - n)) | (~(~(~0 << n) << (p + 1 - n)) & x);
}

这是分解的代码。这看起来还是很糟糕吗?我应该使用这样的变量吗:

int setbits(unsigned x, int p, int n, unsigned y)
{
    unsigned z, k;

    z = y & ~(~0 << n);
    z = z << (p + 1 - n);
    k = ~((~(~0 << n)) << (p + 1 - n));
    k = k & x;

    return z | k;
}

【问题讨论】:

  • 显示您的步数。我不知道你在哪里出错了。
  • ...我也不能。换句话说,该代码不是很容易阅读或理解。也许如果你把它考虑一下,它会变得更清楚?
  • 我确定我的速度很慢,但是u 是什么? (你能证明你的论文也有效吗?)
  • 在这里可以学到一个重要的教训:在一行中编写一个复杂的表达式并不聪明——它使阅读/理解变得困难,甚至更难调试。将您的 setbits 函数重构为几行,每行都包含一个更简单的表达式 - 生成的代码效率不会降低,您将能够在调试器中单步执行代码并检查中间结果。
  • 我想如果我将它们全部分开,我就不需要 100 个括号了,嗯?我会试试看。 K&R 示例和答题簿也都在一行中。

标签: c


【解决方案1】:

措辞有点难以理解,但我认为这就是它所要求的:

罢工>

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

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

细分:

  • |的左半部分

    • ((1&lt;&lt;n)-1) 将向左移动 1 n 位,减去 1 使所有位向右移动 1。
    • &lt;&lt;(p-n) 将它们向左移动 p-n
    • ~ 反转它们,将这些位保留为 0,其他所有位为 1
    • x &amp;x 将这些位变为 0。
  • |的右半部分

    • ((1&lt;&lt;n)-1)同上
    • y &amp; 只会屏蔽这些位
    • &lt;&lt;(p-n) 会将这些位 p-n 向左移动
    • left | right 会将这些值组合在一起,以便:
      • p 位置开始的n 位被设置为y 的最右边的n 位,而x 的其他位保持不变

【讨论】:

  • 这不太对。如果位p..p+nx 中的位,则它们不会改变。
  • @egur ...“保持其他位不变”让我失望
【解决方案2】:

书中的解决方案

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

p=4

x 组件

l1 = ~0 =&gt; 1111_1111_1111_1111_1111_1111_1111_1111
l2 = l1 &lt;&lt; n =&gt; 1111_1111_1111_1111_1111_1111_1111_0000
l3 = ~(l2) =&gt; 0000_0000_0000_0000_0000_0000_0000_1111
l4 = (p + 1 - n) =&gt; 1
l5 = (l3) &lt;&lt; (l4) =&gt; 0000_0000_0000_0000_0000_0000_0001_1110
l6 = ~l5 =&gt; 1111_1111_1111_1111_1111_1111_1110_0001
l7 = x &amp; l6 =&gt; xxxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxx0_000x

y 组件

s1 = ~0 =&gt; 1111_1111_1111_1111_1111_1111_1111_1111
s2 = s1 &lt;&lt; n =&gt; 1111_1111_1111_1111_1111_1111_1111_0000
s3 = ~s2 =&gt; 0000_0000_0000_0000_0000_0000_0000_1111
s4 = (p + 1 - n) =&gt; 1
s5 = s3 &lt;&lt; s4 =&gt; 0000_0000_0000_0000_0000_0000_0001_1110
s6 = y &amp; s5 =&gt; 0000_0000_0000_0000_0000_0000_000y_yyy0

o = l7 | s6

输出

xxxx_xxxx_xxxx_xxxx_xxxx_xxxx_xxxy_yyyx
^all these uncounted bits!!!^ .

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

我们是否从零开始从右侧开始计数,然后一旦到达位置p 就开始向右计数n 位?

unsigned 属于 int 类型,可以使用验证

$ gcc -o ansi-c_2-6 *.c -std=c89
$ ansi-c_2-6

#include <stdio.h>
int main() {
  printf("%s", (sizeof(unsigned) == sizeof(int)) ? "TRUE" : "FALSE");
  return 0;
}

结论

解决方案有误。

解决方案

注意事项

  • bits = CHAR_BIT * sizeof(数据类型))
  • 右移携带最高有效位。 INT_MIN &gt;&gt; 31 == ~0
  • if p + n &gt; 32,你可以施放,但很快就会失控

x东西

左侧

left_mask = INT_MIN &gt;&gt; (p - 1)

右侧

right_mask = INT_MAX &gt;&gt; (p - 1 + n)

x = x &amp; (left_mask | right_mask)

y 东西

中间

mid_mask = ~(left_mask | right_mask)

y = (y &lt;&lt; (bits - p - n)) &amp; mid_mask

return 东西

x | y

TL;DR

书错了

改为这样做

unsigned setbits(unsigned x, unsigned char p, unsigned char n, unsigned y)
{
  char bits = CHAR_BIT * sizeof(unsigned);
  unsigned left_mask, right_mask, mid_mask;
  left_mask = p > 0 ? INT_MIN >> (p-1) : 0;
  right_mask = INT_MAX >> (p - 1 + n);
  mid_mask = (left_mask | right_mask) & x;
  x = (left_mask | right_mask) & x;
  y = y << (bits - p - n) & mid_mask;
  return x | y;
}

限制:

  • n &gt; 0
  • p + n &lt; sizeof(unsigned) * CHAR_BIT

【讨论】:

    【解决方案3】:
    unsigned setbits(unsigned x, int p, int n, int y) {
       return x | (~(~0 << n) << p);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-25
      • 2015-01-14
      • 1970-01-01
      • 1970-01-01
      • 2011-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多