如何在 C 中获取整数的特定位段?

How to get specific bit segments of an integer in C?

给你一个 getTemps() 函数 returns 一个整数组成:每日最高温度 bits 20-29,bits 10-19为每日最低温度,bits 0-9为当前温度,所有 作为 2 的补码 10 位整数。 编写一个 C 程序,提取最高温度、最低温度和当前温度并打印这些值。

我遇到了这种情况。所以我的问题是如何获取整数的特定段。

到目前为止我有:

#include <stdio.h>
unsigned createMask(unsigned a, unsigned b){
    unsigned r = 0;
    unsigned i;
    for (i=a; i<=b; i++){
        r |= 1 << i;

    }
    return r;
}
int main(int argc, char *argv[])
{
  unsigned r = createMask(29,31);
  int i = 23415;
  unsigned result = r & i;
  printf("%d\n", i);
  printf("%u\n", result);
}

例如整数 23415 的二进制为 00000000 00000000 01011011 01110111

然后第 29 位到第 31 位应该是 111 或整数 -1,因为它是 2 的补码。

您可以使用 unionbit field 来完成。尝试这样的事情:

struct TEM_BITS {
    unsigned int tem_high :10;
    unsigned int tem_low  :10;
    unsigned int tem_cur  :10;
};

union TEM_U {
    int tem_values;
    struct TEM_BITS bits;
}

TEM_U t;
t.tem_values = 12345;

printf("tem_high : 0x%X\n", t.bits.tem_high);
printf("tem_low  : 0x%X\n", t.bits.tem_low);
printf("tem_cur  : 0x%X\n", t.bits.tem_cur);

我的位可能倒退了,但下面应该很接近。

int current=x&0x3ff;

int low = (x>>10)&0x3ff;

int high = (x>>20)&0x3ff;

位域 中提取编码信息有三种基本方法。前两个是相关的,区别仅在于 位域结构 的初始化方式。第一个也是最短的方法是简单地创建一个位域结构,定义与该结构的每个成员关联的位。对于用于创建位域的类型,位的总和不能超过 sizeof type * CHAR_BIT 位。一个简单的例子是:

#include <stdio.h>

typedef struct {
    unsigned cur  : 10,
             low  : 10,
             high : 10;
} temps;

int main (void) {

    unsigned n = 0;                 /* encoded number of temps  */

    n = 58;                         /* fill number (58, 46, 73) */
    n |= (46 << 10);
    n |= (73 << 20);

    temps t = *(temps *)&n;         /* initialize struct t      */

    /* output value and temps */
    printf ("\n number entered : %u\n\n", n);

    printf ("   %2hhu - %2hhu  value : %u (deg. F)\n", 0, 9, t.cur);    
    printf ("   %2hhu - %2hhu  value : %u (deg. F)\n", 10, 19, t.low);    
    printf ("   %2hhu - %2hhu  value : %u (deg. F)\n\n", 20, 29, t.high);    

    return 0;
}

注意: memcpy 也可用于初始化结构的值以避免强制转换 n 的地址。 (这里有意这样做是为了避免包含 string.h)。

下一个方法涉及在表示的数据类型和上面讨论的完全相同的结构之间创建 union。使用联合的好处是你可以避免强制类型转换,或者 memcpy 一个值来初始化结构。您只需将编码值分配给联合内的数字类型。使用此方法的相同示例是:

#include <stdio.h>

typedef struct {
    unsigned cur  : 10,
             low  : 10,
             high : 10;
} temps;

typedef union {
    temps t;
    unsigned n;
} utemps;

int main (void) {

    unsigned n = 0;                 /* encoded number of temps  */

    n = 58;                         /* fill number (58, 46, 73) */
    n |= (46 << 10);
    n |= (73 << 20);

    utemps u;                       /* declare/initialize union */
    u.n = n;

    /* output value and temps */
    printf ("\n number entered : %u\n\n", n);

    printf ("   %2hhu - %2hhu  value : %u (deg. F)\n", 0, 9, u.t.cur);    
    printf ("   %2hhu - %2hhu  value : %u (deg. F)\n", 10, 19, u.t.low);    
    printf ("   %2hhu - %2hhu  value : %u (deg. F)\n\n", 20, 29, u.t.high);    

    return 0;
}

最后,第三种方法既不使用 struct 也不使用 union,而仅依赖于 bit shift 操作来完成相同的目的。一个简单的例子是:

#include <stdio.h>
#include <limits.h>     /* for CHAR_BIT */

int main (void) {

    unsigned n = 0;                 /* encoded number of temps  */
    unsigned char i = 0;            /* loop counter             */
    unsigned char r = 10;           /* number of bits in temps  */
    unsigned char s = 0;            /* shift accumulator        */
    unsigned v = 0;                 /* extracted value          */

    n = 58;                         /* fill number (58, 46, 73) */
    n |= (46 << 10);
    n |= (73 << 20);

    printf ("\n number entered : %u\n\n", n);

    /* extract and output temps from n */
    for (i = 0; i < (sizeof n * CHAR_BIT)/r; i++)
    {
        v = (n >> i * r) & 0x3ff;
        printf ("   %2hhu - %2hhu  value : %u (deg. F)\n", s, s + r - 1, v);
        s += r;
    }

    printf ("\n");

    return 0;
}

注意: 您可以使用 createMask 函数自动创建 mask。虽然时间更长,但移位方法的计算量并不大,因为移位操作几乎不需要完成。虽然可以忽略不计,但乘法也可以用移位和加法代替,以进一步调整性能。唯一昂贵的指令是设置循环测试子句的除法,但同样可以忽略不计,所有这些情况都可能被编译器优化。

以上所有示例都产生完全相同的输出:

$ ./bin/bit_extract_shift

 number entered : 76593210

    0 -  9  value : 58 (deg. F)
   10 - 19  value : 46 (deg. F)
   20 - 29  value : 73 (deg. F)