[英]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 & 0x1ffff
比value % 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.