4

这是我的搭档想出的代码,但由于某种原因,我无法让他问他它是如何工作的。我现在已经经历了很多次,似乎无法得到我想得到的答案。

/**
 * bitMask - Generate a mask consisting of all 1's 
 *   lowbit and highbit
 *   Examples: bitMask(5,3) = 0x38
 *   Assume 0 <= lowbit <= 31, and 0 <= highbit <= 31
 *   If lowbit > highbit, then mask should be all 0's
 *   Legal ops: ! ~ & ^ | + << >>
 */
int bitMask(int highbit, int lowbit) {
   int i = ~0;
   return ~(i << highbit << 1) & (i << lowbit);
}
4

2 回答 2

4

这个函数实际上是不正确的:对于较大的highbitand值lowbit,它可能具有特定于实现的行为,甚至是未定义的行为。它应该使用并返回unsigned类型:

unsigned bitMask(int highbit, int lowbit) {
    unsigned i = ~0U;
    return ~(i << highbit << 1) & (i << lowbit);
}

以下是步骤:

  • i = ~0U;将 i 设置为所有位 1。

  • i << highbit将这些位向左移动,highbit在低位插入 0 位。

  • i << highbit << 1为多一个 0 位腾出空间。不应该简化这个表达式,i << (highbit + 1)因为这样的位移是实现定义的,如果highbit + 1变得更大或等于i.

  • ~(i << highbit << 1)补充这个掩码,创建一个掩码,其highbit + 1位设置在低位,0 位设置为高位。

  • i << lowbit创建一个具有lowbit0 位和 1 在较高位置的掩码。

  • ~(i << highbit << 1) & (i << lowbit)计算这 2 个掩码的交集,结果从位数lowbit到位数(highbit含)有 1 位,从0最低有效位开始编号。

例子:

  • bitMask(31, 0)-> 0xFFFFFFFF
  • bitMask(0, 0)-> 0x00000001
  • bitMask(31, 16)-> 0xFFFF0000
  • bitMask(15, 0)-> 0x0000FFFF

这种编号方法用于硬件规范。我个人更喜欢另一种方法,其中指定要跳过的位数和要设置的位数,更符合位字段规范:

unsigned bitSpec(int start, int len) {
    return (~0U >> (32 - len)) << start;
}

和相同的例子:

  • bitSpec(0, 32)-> 0xFFFFFFFF
  • bitSpec(0, 1)-> 0x00000001
  • bitSpec(16, 16)-> 0xFFFF0000
  • bitSpec(0, 16)-> 0x0000FFFF
于 2016-01-31T02:54:42.050 回答
1

在您的情况下,鉴于您的函数中包含的描述,该函数正在执行您似乎打算执行的操作。主要问题是您使用int的是unsigned int. 这将导致符号扩展出现问题。(更不用说缺乏 C 中带符号移位的定义)。

一个简单的转换unsigned将显示它正在按预期运行:

简短的例子:

#include <stdio.h>
#include <stdlib.h>

unsigned int bitMask (unsigned int highbit, unsigned int lowbit) {
    unsigned int i = ~0;
    return ~(i << highbit << 1) & (i << lowbit);
}

char *binstr (unsigned long n, unsigned char sz, unsigned char szs, char sep) {

    static char s[128 + 1] = {0};
    char *p = s + 128;
    unsigned char i;

    for (i = 0; i < sz; i++) {
        p--;
        if (i > 0 && szs > 0 && i % szs == 0)
            *p-- = sep;
        *p = (n >> i & 1) ? '1' : '0';
    }

    return p;
}

int main (int argc, char **argv) {

    unsigned high = argc > 1 ? (unsigned)strtoul (argv[1], NULL, 10) : 5;
    unsigned low  = argc > 2 ? (unsigned)strtoul (argv[2], NULL, 10) : 3;

    printf ("%s\n", binstr (bitMask (high, low), 32, 8, '-'));

    return 0;
}

输出

$ ./bin/bitmask
00000000-00000000-00000000-00111000

$ ./bin/bitmask 10 3
00000000-00000000-00000111-11111000

$ ./bin/bitmask 31 5
11111111-11111111-11111111-11100000

$ ./bin/bitmask 4 8
00000000-00000000-00000000-00000000
于 2016-01-31T02:49:48.120 回答