【问题标题】:Bit invert function in K&R exercise 2-7K&R 练习 2-7 中的位反转功能
【发布时间】:2013-04-01 16:19:14
【问题描述】:

C 编程语言练习 2-7

编写一个函数invert(x,p,n),它返回x,其中n位开始于p位置反转(即1更改为0,反之亦然),其他保持不变。

我理解这样的问题:我有 182,即二进制的 101(101)10,括号中的部分必须反转而不改变其余部分。那么返回值应该是10101010,即十进制的170。

这是我的尝试:

#include <stdio.h>

unsigned int getbits(unsigned int bitfield, int pos, int num);
unsigned int invert(unsigned int bitfield, int pos, int num);

int main(void)
{
    printf("%d\n", invert(182, 4, 3));
    return 0;
}

/* getbits: get num bits from position pos */
unsigned int getbits(unsigned int bitfield, int pos, int num)
{
    return (bitfield >> (pos+1-n)) & ~(~0 << num);
}

/* invert: flip pos-num bits in bitfield */
unsigned int invert(unsigned int bitfield, int pos, int num)
{
    unsigned int mask;
    unsigned int bits = getbits(bitfield,pos,num);

    mask = (bits << (num-1)) | ((~bits << (pos+1)) >> num);

    return bitfield ^ mask;
}

这似乎是正确的(对我来说),但 invert(182, 4, 3) 输出 536870730getbits() 工作正常(直接来自书中)。我写下了我分配给y的表达式中发生的事情:

(00000101 << 2) | ((~00000101 << 5) >> 3)    -- 000000101 is the part being flipped: 101(101)10
       00010100 | ((11111010 << 5) >> 3)
       00010100 | (01000000 >> 3)
       00010100 | 00001000
= 00011100

  10110110 (182)
^ 00011100
----------
= 10101010 (170)

应该是正确的,但事实并非如此。我发现这是出错的地方:((~xpn &lt;&lt; (p+1)) &gt;&gt; n)。我不明白怎么做。

另外,我不知道这段代码有多通用。我的首要任务是让这个案子运作起来。也欢迎在这个问题上提供帮助。

【问题讨论】:

  • 不要给变量取诸如“x, p, n”之类的垃圾名称,这是一个非常糟糕的主意。
  • @Lundin K&R 这样命名它们,所以我也这样做了。尽管这背后可能有格式化的原因。我会记住这一点的。
  • 是的,我意识到真正糟糕的想法来自 K&R。请注意,您无法从那本书中学到好的编程实践。对于已经过时 14 年的 C 语言版本,你只能从中学习语法。
  • @Lundin,这些变量名很好。 K&R 是正确的。根据定义。或者查看 Linux 内核 C 风格指南。
  • @vonbrand Err,不,我很抱歉,但在现代编程中,没有“定义正确,因为某些大师在 80 年代这样说”这样的东西。此外,程序员应该使用自己的大脑。如果您认为“x”是一个很棒的变量名,无论是否使用您的大脑,那就这样吧-在得出这样的结论之后不再讨论是没有意义的。至于 Linux 内核编码风格,它是一个相当非正式的文档,对于所陈述的模糊规则缺乏大量理由或来源。这当然不是某些 C 规范。如果您想要专业的编码标准,请查看 MISRA-C 或 CERT-C。

标签: c bit-manipulation kernighan-and-ritchie


【解决方案1】:

((1u&lt;&lt;n)-1) 是一个位掩码,在 RHS 处带有 n '1' 位。 &lt;&lt;p 将此块 p 位置向左移动。 (如果你想从左边数,你应该用(p-n)而不是p来移动)。

return val  ^ (((1u<<n)-1) <<p) ;

当 p 大于 wordsize 时仍然存在问题(移动超过 wordsize 是未定义的),但这应该是调用者的责任;-)

例如101(101)10,p=2 和 n=3:

1u<<n               := 1000
((1u<<n)-1)         := 0111
(((1u<<n)-1) <<p) := 011100
original val    := 10110110
val ^ mask      := 10101010

【讨论】:

  • 第一行是干什么用的?该条件也适用于 0
  • 不需要。 (我害怕面具变成全1,但是(1&lt;&lt;0)-1全是零。
  • 似乎也没有给出正确的答案。我现在澄清了我如何理解我的问题中的练习。当然,我可能理解错了。
  • 那是counting from the right 部分。在您的 101(101)10 示例中,我假设 p=2,n=3。
  • @wildplasser 我刚刚意识到我从右边数到了p,但从左数到了n
【解决方案2】:

我认为您在其中一个班次中遇到了一个问题(这只是一种预感,我不完全确定)。不过,我会保持简单(我假设索引位置 p 从 LSB 开始,即 p=0 是 LSB):

unsigned int getbits(unsigned int x, int p, int n) {
  unsigned int ones = ~(unsigned int)0;
  return x ^ (ones << p) ^ (ones << (p+n));
}

编辑:如果您需要 p=0 作为 MSB,只需反转移位(这可以正常工作,因为 ones 被定义为 unsigned int):

unsigned int getbits(unsigned int x, int p, int n) {
  unsigned int ones = ~(unsigned int)0;
  return x ^ (ones >> p) ^ (ones >> (p+n));
}

注意:在这两种情况下,如果 p &lt; 0p &gt;= sizeof(int)*8p+n &lt; 0p+n &gt;= sizeof(int)*8 getbits 的结果是未定义的。

【讨论】:

  • 没有给出正确的值。我正在使用 182 进行测试,即101(101)10 - 括号中的部分是应该倒置的部分。 invert(182, 4, 3) 应该得到的值是 170。
  • 这是因为您将 MSB 计为 p=0。我明确表示我将 LSB 计为 p=0。我会修正我的答案。
  • 我的错误。我从右边数p,但从左边数n
【解决方案3】:

看看 Steve Summit 的 "Introductory C programming" 和 Ted Jensen 的 "At tutorial on pointers and arrays in C"。他们所涵盖的语言与今天的 C 语言有些不同(编程习惯也在演变,机器大大更大,真正的人不再编写汇编程序了),但他们所说的大部分内容都是真实的今天和当时一样。肖恩·安德森的"Bit twiddling hacks" 会让你眼花缭乱。保证。

【讨论】:

    【解决方案4】:

    我发现我的实现中出了什么问题(除了从错误的方向计数num)。现在我已经了解了更多关于位的知识,之后似乎相当明显。

    当一个 1 位左移,超出位域范围时,它被扩展。

       1000 (8) << 1
    == 10000 (16)
    

    bitfield &lt;&lt; nbitfield 乘以 2 n 倍。我的表达式 ((~bits &lt;&lt; (pos+1)) &gt;&gt; num) 分别有 5、4 和 3 作为 bitsposnum 的值。我将一个几乎 32 位 int 大小的数字乘以 2,两次。

    【讨论】:

      【解决方案5】:

      我的功能怎么样?我觉得很好。

      unsigned invert(unsigned x,int p,int n)
      {
           return (x^((~(~0<<n))<<p+1-n));
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-28
        • 1970-01-01
        • 1970-01-01
        • 2010-09-29
        相关资源
        最近更新 更多