繁体   English   中英

如何在 C 中提取 32 位无符号整数的特定“n”位?

[英]How do I extract specific 'n' bits of a 32-bit unsigned integer in C?

谁能告诉我如何从 C 中的 32 位无符号整数中提取“n”个特定位。

例如,假设我想要 32 位值的前 17 位; 我应该怎么做?
我想我应该使用模数运算符,我尝试了它并且能够获得最后 8 位和最后 16 位作为

unsigned last8bitsvalue=(32 bit integer) % 16
unsigned last16bitsvalue=(32 bit integer) % 32

这个对吗? 有没有更好更有效的方法来做到这一点?

与其将其视为“提取”,我更喜欢将其视为“隔离”。 一旦隔离了所需的位,您就可以对它们进行任何操作。

要隔离任何一组位,请应用 AND 掩码。

如果您想要一个值的最后 X 位,可以使用一个简单的技巧。

unsigned  mask;
mask = (1 << X) - 1;
lastXbits = value & mask;

如果你想隔离从'startBit'开始的'value'中间的一系列X位......

unsigned  mask;
mask = ((1 << X) - 1) << startBit;
isolatedXbits = value & mask;

希望这可以帮助。

如果您想要特定的 n 位,那么您可以首先创建一个位掩码,然后将其AND您的数字相加以获取所需的位。

创建从位 a 到位 b 的掩码的简单函数。

unsigned createMask(unsigned a, unsigned b)
{
   unsigned r = 0;
   for (unsigned i=a; i<=b; i++)
       r |= 1 << i;

   return r;
}

您应该检查 a<=b。

如果您想要第 12 位到第 16 位调用该函数,然后只需 & (逻辑与) r与您的数字N

r = createMask(12,16);
unsigned result = r & N;

如果你愿意,你可以改变结果。 希望这可以帮助

Intel 和 AMD CPU 上有一个BEXTR(位域提取(带寄存器)) x86 指令,ARM 上有UBFX 有一些内在函数,例如_bextr_u32() (链接需要登录),允许显式调用此指令。

他们实现(source >> offset) & ((1 << n) - 1) C 代码:从offset位开始从source获取n连续位。 这是处理边缘情况的完整函数定义:

#include <limits.h>

unsigned getbits(unsigned value, unsigned offset, unsigned n)
{
  const unsigned max_n = CHAR_BIT * sizeof(unsigned);
  if (offset >= max_n)
    return 0; /* value is padded with infinite zeros on the left */
  value >>= offset; /* drop offset bits */
  if (n >= max_n)
    return value; /* all  bits requested */
  const unsigned mask = (1u << n) - 1; /* n '1's */
  return value & mask;
}

例如,要从第5位开始从2273 ( 0b100011100001 ) 中获取3位,请调用getbits(2273, 5, 3) — 它会提取 7 ( 0b111 )。

例如,假设我想要 32 位值的前 17 位; 我应该怎么做?

unsigned first_bits = value & ((1u << 17) - 1); // & 0x1ffff

假设CHAR_BIT * sizeof(unsigned)在您的系统上为 32。

我想我应该使用模数运算符,我试过了,能够得到最后 8 位和最后 16 位

unsigned last8bitsvalue  = value & ((1u <<  8) - 1); // & 0xff
unsigned last16bitsvalue = value & ((1u << 16) - 1); // & 0xffff

如果在问题中的所有示例中偏移量始终为零,那么您不需要更通用的getbits() 有一个特殊的 cpu 指令 BLSMSK 有助于计算掩码((1 << n) - 1)

模数(仅)用于获取底部位,尽管我认为value & 0x1ffffvalue % 131072更直接地表达“取底部 17 位”,因此这样做更容易理解。

32 位无符号值的前 17 位将是value & 0xffff8000 (如果您希望它们仍然在顶部的位置),或者如果您希望值的前 17 位在底部 17 位中,则value >> 15结果。

如果您需要整数的最后 X 位,请使用二进制掩码

unsigned last8bitsvalue=(32 bit integer) & 0xFF
unsigned last16bitsvalue=(32 bit integer) & 0xFFFF

这是已接受答案的更简短的变体:下面的函数通过创建位掩码来提取从到包含的位。 在对原始数字应用 AND 逻辑后,结果被移位,因此函数只返回提取的位。 为了清楚起见,跳过了索引/完整性检查。

uint16_t extractInt(uint16_t orig16BitWord, unsigned from, unsigned to) 
{
  unsigned mask = ( (1<<(to-from+1))-1) << from;
  return (orig16BitWord & mask) >> from;
}

按位与您的整数与掩码正好具有您想要提取的那些位集。 如果需要,然后将结果右移以重新定位提取的位。

unsigned int lowest_17_bits = myuint32 & 0x1FFFF;
unsigned int highest_17_bits = (myuint32 & (0x1FFFF << (32 - 17))) >> (32 - 17);

编辑:后者将最高 17 位重新定位为最低 17; 如果您需要从较大的整数“内部”提取整数,这将很有用。 如果不需要,您可以省略右移 ( >> )。

#define GENERAL__GET_BITS_FROM_U8(source,lsb,msb) \
    ((uint8_t)((source) & \
        ((uint8_t)(((uint8_t)(0xFF >> ((uint8_t)(7-((uint8_t)(msb) & 7))))) & \
             ((uint8_t)(0xFF << ((uint8_t)(lsb) & 7)))))))

#define GENERAL__GET_BITS_FROM_U16(source,lsb,msb) \
    ((uint16_t)((source) & \
        ((uint16_t)(((uint16_t)(0xFFFF >> ((uint8_t)(15-((uint8_t)(msb) & 15))))) & \
            ((uint16_t)(0xFFFF << ((uint8_t)(lsb) & 15)))))))

#define GENERAL__GET_BITS_FROM_U32(source,lsb,msb) \
    ((uint32_t)((source) & \
        ((uint32_t)(((uint32_t)(0xFFFFFFFF >> ((uint8_t)(31-((uint8_t)(msb) & 31))))) & \
            ((uint32_t)(0xFFFFFFFF << ((uint8_t)(lsb) & 31)))))))
int get_nbits(int num, int n)
{
return  (((1<<n)-1) & num);
}

我有另一种方法来实现这一点。 您可以使用整数类型的union ,该联合具有足够的位用于您的应用程序和位字段struct

例子:

typedef thesebits
{

  unsigned long first4    : 4;
  unsigned long second4   : 4;
  unsigned long third8    : 8;
  unsigned long forth7    : 7;
  unsigned long fifth3    : 3;
  unsigned long sixth5    : 5;
  unsigned long last1     : 1;

} thesebits;

您可以将该struct设置为您想要的任何位模式。 如果你有多个位模式,你甚至可以在你的联合中使用它。

typedef thesebitstwo
{

  unsigned long first8    : 8;
  unsigned long second8   : 8;
  unsigned long third8    : 8;
  unsigned long last8     : 8;

} thesebitstwo;

现在你可以建立你的工会:

typedef union myunion 
{
   unsigned long mynumber;
   thesebits     mybits;
   thesebitstwo  mybitstwo;
} myunion;

然后,您可以从分配给成员 mynumber 的任何数字中访问所需的位:

myunion getmybits;
getmybits.mynumber = 1234567890;

如果你想要最后 8 位:

last16bits = getmybits.mybitstwo.last8;

如果你想要第二个 4 位:

second4bits = getmybits.mybits.second4;

我举了两个例子来展示随机分配的不同位。 您可以为想要获取的任何位设置结构位域。 我将所有变量的类型设为unsigned long ,但您可以使用任何变量类型,只要位数不超过该类型中可以使用的位数。 所以其中大部分可能只是unsigned int ,有些甚至可能是unsigned short

这里需要注意的是,如果您总是想要一遍又一遍地使用相同的一组位,那么这是可行的。 如果出于某种原因,您可能需要更改要查看的位,则可以使用带有数组的结构来保留位的副本,如下所示:

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>

typedef struct bits32
{

    bool b0              : 1;
    bool b1              : 1;
    bool b2              : 1;
    bool b3              : 1;
    bool b4              : 1;
    bool b5              : 1;
    bool b6              : 1;
    bool b7              : 1;
    bool b8              : 1;    
    bool b9              : 1;
    bool b10             : 1;
    bool b11             : 1;
    bool b12             : 1;    
    bool b13             : 1;
    bool b14             : 1;
    bool b15             : 1;
    bool b16             : 1;    
    bool b17             : 1;
    bool b18             : 1;
    bool b19             : 1;
    bool b20             : 1;    
    bool b21             : 1;
    bool b22             : 1;
    bool b23             : 1;
    bool b24             : 1;    
    bool b25             : 1;
    bool b26             : 1;
    bool b27             : 1;
    bool b28             : 1;    
    bool b29             : 1;
    bool b30             : 1;
    bool b31             : 1;

} bits32;

typedef struct flags32 {
    union 
    {
    
        uint32_t        number;
        struct bits32   bits;
    
    };
    bool                    b[32];
} flags32;

struct flags32 assignarray ( unsigned long thisnumber )
{
    struct flags32  f;
    f.number = thisnumber;
    
    f.b[0] = f.bits.b0;
    f.b[1] = f.bits.b1;
    f.b[2] = f.bits.b2;
    f.b[3] = f.bits.b3;
    f.b[4] = f.bits.b4;
    f.b[5] = f.bits.b5;
    f.b[6] = f.bits.b6;
    f.b[7] = f.bits.b7;
    f.b[8] = f.bits.b8;
    f.b[9] = f.bits.b9;
    f.b[10] = f.bits.b10;
    f.b[11] = f.bits.b11;
    f.b[12] = f.bits.b12;
    f.b[13] = f.bits.b13;
    f.b[14] = f.bits.b14;
    f.b[15] = f.bits.b15;
    f.b[16] = f.bits.b16;
    f.b[17] = f.bits.b17;
    f.b[18] = f.bits.b18;
    f.b[19] = f.bits.b19;
    f.b[20] = f.bits.b20;
    f.b[21] = f.bits.b21;
    f.b[22] = f.bits.b22;
    f.b[23] = f.bits.b23;
    f.b[24] = f.bits.b24;
    f.b[25] = f.bits.b25;
    f.b[26] = f.bits.b26;
    f.b[27] = f.bits.b27;
    f.b[28] = f.bits.b28;
    f.b[29] = f.bits.b29;
    f.b[30] = f.bits.b30;
    f.b[31] = f.bits.b31;

    return f;
    
}

int main ()

{

    struct flags32 bitmaster;
    bitmaster = assignarray(1234567890);

    printf("%d\n", bitmaster.number);
    printf("%d\n",bitmaster.bits.b9);
    printf("%d\n",bitmaster.b[9]);
    
    printf("%lu\n", sizeof(bitmaster));
    printf("%lu\n", sizeof(bitmaster.number));
    printf("%lu\n", sizeof(bitmaster.bits));
    printf("%lu\n", sizeof(bitmaster.b));
    
}

最后一个例子的问题是它不紧凑。 联合本身只有 4 个字节,但是由于您不能做指向位域的指针(没有复杂且有争议的“非标准”代码),因此该数组会复制每个布尔值并为每个布尔值使用一个完整字节一,而不仅仅是一点点,所以它占用了总内存空间的 9 倍(如果你运行我给出的 printf 语句示例,你会看到)。

但是现在,您可以逐位寻址并使用变量来索引每个位,如果您不缺内存,那就太好了。

通过使用上面的 typedefs 和assignarray函数作为flags32的构造函数,您可以轻松扩展为多个变量。 如果您可以仅使用 .b# 寻址并且无法使用变量,则可以将联合定义为flags32并省略结构的其余部分。 然后你也不需要assignarray函数,你会使用更少的内存。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM