在C语言中进行位运算:掩码和提取位

5

我一直在看有关掩码的帖子,但我仍然无法理解如何从C中提取某些位。

比如说我们有一个整数,0001 1010 0100 1011,它的十六进制表示是0x1A4B,对吧?如果我想知道第5到第7个数字,也就是这种情况下的101,我应该使用int mask = 0x0000 1110 0000 0000, int extract = mask&number吗?

另外,我如何检查它是否为101?我猜==在这里行不通...


如果你想要检查第5和第7个数字是否为101,则将掩码保留为int mask= 0x0000 1010 0000 0000,然后执行ExNOR操作。如果提取第5和第7位是111,那么它就是101 - Haris
1
这个 0x0000 1110 0000 是什么意思?如果你想展示一个位序列,这是错误的,因为 0x 表示一个十六进制字面量。 - alk
一些经典的问题包括 什么是位掩码? 和 *如何设置、清除和切换单个位?*。 - Peter Mortensen
7个回答

6

掩码是通过将除了你想要的一位数之外的所有位都设置为0来完成的。所以,假设您有一个8位变量,并且您想要检查从中第5个位是否为1。假设您的变量是00101100。为了屏蔽所有其他位,我们使用&运算符将除第5位之外的所有位都设置为0:

00101100 & 00010000

现在这个操作是针对除了第5位之外的每一个比特,将右边的字节的比特设置为0,所以&运算的结果将会是0。然而,对于第5位比特,它来自右边的比特是1,因此结果将会是从左边的字节中第5位的值 - 在这个例子中为0。
现在要检查这个值,您需要将其与某些东西进行比较。要做到这一点,只需将结果与右侧的字节进行比较。
result = (00101100 & 00010000) == 00000000

为了概括这个过程,您可以通过左移00000001直到获得所需位来从左侧字节检索任何位。以下函数实现了此功能:
int getBit(char byte, int bitNum)
{
    return (byte & (0x1 << (bitNum - 1)))
}

这适用于任何大小的变量,无论是8、16、32还是64(或其他任何大小)。

5
假设使用GCC扩展0b来定义二进制文字:
int number = 0b0001101001001011; /* 0x1A4B */
int mask =   0b0000111000000000; /* 0x0E00 */
/* &'ed:     0b0000101000000000;    0x0A00 */
int extract = mask & number;     /* 0x0A00 */

if (extract == 0b0000101000000000)
/* Or if 0b is not available:
if (extract == 0x0a00 ) */
{
  /* Success */
}
else
{
  /* Failure */
}


1
首先,二进制中的数字(通常)从右边开始计数(第10和12位),或者你可以说是第5和第7个最重要的数字。
int mask =  0x0E00;  // 0000 1110 0000 0000;
int extract = mask & number;

结果为:
extract = 0000 1010 0000 0000

你可以做到。
if (extract == 0x0A00 /*0000 1010 0000 0000*/){}

测试一下,或者:

if (( extract >> 9 ) == 0x05){}

对于你提供的示例数字,if语句中的两个语句都将返回true。

通常情况下,使用掩码测试单个数字。您可以使用以下函数进行测试:

bool digit_value( unsigned int number, unsigned int digit)
{
    return (1 << digit) & number;
}

int main()
{
    unsigned int number = 0x1A4B;
    int should_be_three = 0;
    should_be_three +=  digit_value(number, 10);
    should_be_three += !digit_value(number, 11);
    should_be_three +=  digit_value(number, 12);
    printf("%s", (should_be_three == 3?"it worked":"it didn't work"));
    return 0;
}

1
你的比较是错误的;不是在与十六进制0x101进行比较,而是与二进制101进行比较,它等于0x05。 - sirlark
谢谢!但是用 if (( extract >> 9 ) == 0x101)x101 实际上是 0001 0000 0001 吗?它应该是 if (( extract >> 9 ) == 0b101) 吗? - stillAFanOfTheSimpsons
是的,我做错了一点。现在已经修复了。(使用纯十六进制) - Baldrickk

1
你需要进行掩码和移位操作。要么将你要比较的值进行移位,要么将你要比较的值进行移位。我发现将你要比较的值进行移位更容易理解。所以,如果你想提取从左边数第5到第7个数字,你需要向右移动9个位置(16-7),这样第7个数字就成了最右边的数字,然后应用0x7(二进制中的111)作为掩码,只获取最右边的三个二进制数字。
int i = 0x1A4B;
if (((i >> 9) & 0x07) == 0x05) { // 0x05 = 101 in binary
    //do what you need to
}

你移出了相关的位。 - vlad_tepesch
没有,他是从左边开始数的,不是右边! - sirlark
@vlad_tepesch:这不是回答了问题吗? - sirlark

0

我可以使用int mask= 0x0000 1110 0000 0000int extract = mask&number吗?

是的,你可以这样做。

另外,如何检查它是否为101

你可以检查这个- 0000 1010 0000 0000,在int中是1280

extract== 1280

0

逐位检查比一次性检查所有位更简单。 首先,您需要为感兴趣的位创建掩码:

int fifthBitMask = 1 << 4;
int fifthBitResult = number & fifthBitMask;

int seventhBitMask = 1 << 6;
int seventhBitResult = number & seventhBitMask;

现在,您可以将结果与零或掩码进行比较。 与零比较可以省略,因此您只需使用简单的if语句:

if (fifthBitResult && seventhBitResult)
{
    //your code here
}

此外,您还可以与掩码进行比较。在操作&之后,结果将仅设置在掩码中已设置的位。 因此,它可能像这样: if (fifthBitResult == fifthBitMask && seventhBitResult == seventhBitMask) { // 在此处编写您的代码 }

因此,如果操作的结果等于掩码,则可以使用一次操作执行此操作:

int mask = 0x5 << 4; // 0x5 is hex representation of 101b
int result = number & mask;
if (result == mask)
{
    // your code here
}

0

首先,你对7-6-5位的计算是错误的。你说它是101,但实际上应该是010(对于x1a43)。

其次,要获取这些位(这些位所表示的值),你应该执行&0xE0

int my_bits_from_5to7 = number & 0xE0;


取决于你从哪一端开始计数,也取决于你是从0还是1开始计数。 - ilent2
@ilent2 不对,在大多数体系结构上,位顺序始终相同:从右到左,第一个最低位始终为0号。 - Ruslan Gerasimov
如果提问者不熟悉位运算,那么他们很可能也不了解惯例,例如从零开始计数和从右到左(而英语是从左到右阅读的)。因此,这个说法是正确的。 - ilent2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接