我对C编程还比较新,最近接触到了位掩码,想了解一下它的一般概念和功能。
提供一些例子将更容易理解。
我对C编程还比较新,最近接触到了位掩码,想了解一下它的一般概念和功能。
提供一些例子将更容易理解。
掩码用于定义您想要保留哪些位和您想要清除哪些位。
掩码操作是将掩码应用于值的过程。这可以通过以下方式完成:
以下是一个从值中提取一部分位的示例:
Mask: 00001111b
Value: 01010101b
应用掩码到值上意味着我们想要清除前4位(高位),并保留最后4位(低位)。 因此,我们已经提取了低4位。 结果是:
Mask: 00001111b
Value: 01010101b
Result: 00000101b
掩码是使用AND实现的,因此在C中我们得到:
uint8_t stuff(...) {
uint8_t mask = 0x0f; // 00001111b
uint8_t value = 0x55; // 01010101b
return mask & value;
}
以下是一个相当常见的用例:从一个较大的字中获取单个字节。我们将字中的高位比特定义为第一个字节。我们使用两个运算符来实现这一点,&
和>>
(向右移位)。以下是如何从32位整数中提取四个字节的方法:
void more_stuff(uint32_t value) { // Example value: 0x01020304
uint32_t byte1 = (value >> 24); // 0x01020304 >> 24 is 0x01 so
// no masking is necessary
uint32_t byte2 = (value >> 16) & 0xff; // 0x01020304 >> 16 is 0x0102 so
// we must mask to get 0x02
uint32_t byte3 = (value >> 8) & 0xff; // 0x01020304 >> 8 is 0x010203 so
// we must mask to get 0x03
uint32_t byte4 = value & 0xff; // here we only mask, no shifting
// is necessary
...
}
请注意,您可以交换上述运算符的顺序,您可以首先进行掩码操作,然后进行移位操作。结果是相同的,但现在您必须使用不同的掩码:
uint32_t byte3 = (value & 0xff00) >> 8;
掩蔽是指保留、更改或删除所需信息的一部分。让我们看一个图像掩蔽操作; 就像这个掩蔽操作正在去除不是皮肤的任何东西:
在此示例中,我们执行了一个与运算。还有其他掩蔽运算符 - 或和XOR。
位掩蔽是指对位进行掩蔽。这里是一个带有与运算的位掩蔽 -
1 1 1 0 1 1 0 1 input
(&) 0 0 1 1 1 1 0 0 mask
------------------------------
0 0 1 0 1 1 0 0 output
因此,只有中间四个位(因为这些位在此掩码中为1
)保留。
让我们使用XOR来看一下——
1 1 1 0 1 1 0 1 input
(^) 0 0 1 1 1 1 0 0 mask
------------------------------
1 1 0 1 0 0 0 1 output
现在,中间的四个比特被翻转了(1
变成了 0
,0
变成了 1
)。
因此,使用位掩码可以访问单个位 (示例)。有时,这种技术也可用于提高性能。以这个为例-
bool isOdd(int i) {
return i%2;
}
这个函数用来判断一个整数是奇数还是偶数。我们可以使用位掩码更有效地实现相同的结果 —
bool isOdd(int i) {
return i&1;
}
简要解释:如果二进制数的最低有效位(LSB)是1
,则该数为奇数;如果是0
,则为偶数。因此,通过与1
进行AND操作,我们可以删除除最低有效位以外的所有其他位,即:
55 -> 0 0 1 1 0 1 1 1 input
(&) 1 -> 0 0 0 0 0 0 0 1 mask
---------------------------------------
1 <- 0 0 0 0 0 0 0 1 output
位和字节
在计算机中,数字以二进制形式内部表示。这意味着,当您为变量使用整数类型时,它实际上将被表示为零和一的总和。
正如您所知,一个比特表示一个0
或一个1
。八个这些位的串联表示一个字节,例如00000101
,它是数字5。
我假设您知道数字如何以二进制形式表示,如果不知道,请看看这里。
在PHP中,一个数字(大多数情况下)有4个字节长。这意味着您的数字实际上使用了32位的内部存储。但出于简单起见,在本答案中,我将使用8位数字。
使用位存储状态
现在想象一下,你想要创建一个程序来保存状态,该状态基于多个值,这些值是一个(true
)或零(false
)。可以将这些值存储在不同的变量中,无论是布尔型还是整数型。或者使用单个整数变量,并使用其内部32位的每个位来表示不同的true和false。
一个例子:00000101
。这里从右到左读取第一位(reading from right to left)为true,表示第一个变量。第二位为false,表示第二个变量。第三位为true。以此类推...
这是一种非常紧凑的存储数据的方式,有许多用途。
位掩码
这就是位掩码发挥作用的地方。听起来很复杂,但实际上很简单。
位掩码允许您使用在位级别上工作的操作。
实际上,您将一个掩码(mask)应用到一个值上,在我们的情况下,该值是我们的状态00000101
,而掩码是再次是一个二进制数,它指示感兴趣的位(bit)。
通过对掩码和状态进行二进制操作,可以实现以下功能:
如果我们想将特定值设置为true,则可以使用OR运算符和以下位掩码:
Mask: 10000000b
Value: 00000101b
---- OR ---------
Result: 10000101b
或者可以使用 AND 运算符从状态中选择特定的值:
Mask: 00001100b
Value: 00000101b
---- AND ---------
Result: 00000100b
这只是一个在二进制中表示的数字。例如,假设我有8个布尔值(true
或 false
)要存储。我可以将其存储为8个布尔值的数组,或者我可以将其存储为一个单独的字节(8位),每个位都存储其中一个布尔值(0
= false
,1
= true
)。
此时,我可以轻松操纵我的字节,以便我可以(1)设置特定的位开或关(true或false),以及(2)检查特定的位是否开或关。
mask = mask | (1 << bitIndex)
mask = mask & ~(1 << bitIndex)
(mask & (1 << bitIndex)) != 0
所有这些操作都使用左移运算符,它将位从最低有效位置移动到最高有效位置。
实质上,Bitmask 是一个布尔标志列表(例如 isAlive、isMoving 等)压缩成一个单一字段,通常是一个整数。它可以显著减少 JSON 字符串大小或内存占用。
这在 PHP 中尤其重要,因为数组中的单个布尔值可能占用与整数相同的 RAM。有一个非常简单的 Bitmask 指南,它将逐步解释您需要了解的所有内容,包括如何以及何时使用它。
编辑:这里是一个存档链接:https://archive.is/QwCUX,因为原始网站似乎已经关闭。
Base =
{
"A": "baseA",
"B": "baseB",
}
Mask =
{
"A": "maskA",
"C": "maskC",
}
==>
{
"A": "maskA", // Masked / overridden by the mask
"B": "baseB", // Pass through from base because not masked
"C": "maskC", // Added because present in the mask (null/undefined in base)
}
例2 数组 假设null
是一个哨兵值,而不是有效值
base = ["base1", "base2"]
mask = [null, "mask2", "mask3"]
==> ["base1", "mask2", "mask3"]
例3 树 假设null
是一个金丝雀值,而不是有效值
Base =
B
/
A
\
C
Mask =
E
/
D
/
null
Output =
E // Added by presence in mask
/
D // Override base value because present in mask
/
A // Passthrough from base because `null` in mask means pass through
\
C // pass through because undefined in mask
EG 4 字符串
我的同事告诉我,在计算机中“mask”一词还有另一个关键用途,可应用于字符串,常用于日志。该掩码通常不会被显式地定义为输入值,而更像是一种函数,可以屏蔽敏感数据。
base = "[INFO] user logged in with user=stackoverflow@example.com password=123456"
mask(base) ==> "[INFO] user logged in with user=stackoverflow@example.com password=******"
(有时不匹配掩码长度以隐藏所掩盖的项目的长度)
请查看:https://github.com/gwpmad/mask-deep,这是使用“mask”一词的示例。