问题描述
此算法可通过将指数递增的数字值分配给各个值来“散列”或将多个值编码为单个整数。这种方法尤其在Windows DLL中使用。
可能的用例可以是客户端应用程序从API请求与某些状态代码匹配的项列表。
例如,如果我们具有以下值:
* open
* assigned
* completed
* closed
...我们为每个数字分配一个数值:
* open - 1
* assigned - 2
* completed - 4
* closed - 8
等其后每个值是前一个值的2倍。
编码
当我们需要传递这些值中的任何一个的组合时,我们会将相应的数值相加。例如,对于“打开,已分配”,它是3
,对于“已分配,已完成,关闭”,它是14
。这涵盖了所有独特的组合。如我们所见,“编码”部分非常简单。
解码
要解码该值,我唯一想到的方法是switch..case语句,例如(伪代码):
1 = open
2 = assigned
3 = open + assigned
4 = completed
5 = open + completed
6 = assigned + completed
7 = open + assigned + completed
8 = closed
9 = open + closed
10 = assigned + closed
11 = open + assigned + closed
12 = completed + closed
13 = open + completed + closed
14 = assigned + completed + closed
15 = open + assigned + completed + closed
此算法显然可以在以下假设下工作:
- 仅在每个值仅使用一次时起作用
- 仅在双方都知道匹配的数值时才起作用
问题:
注意:该问题用winapi
标记的主要是为了发现问题。该算法相当通用。
解决方法
您所描述的内容被正式称为bit mask,其中整数中的每个位都分配有一个含义。这些位被分配为二进制2的幂(bit0 = 2 0 = 1,bit1 = 2 1 = 2,bit2 = 2 2 = 4,bit3 = 2 3 = 8等)。
您可以使用OR
和AND
logical bitwise operators来设置/查询整数中的各个位,例如:
const DWORD State_Open = 1;
const DWORD State_Assigned = 2;
const DWORD State_Completed = 4;
const DWORD State_Closed = 8;
void DoSomething(DWORD aStates)
{
...
if (aStates & State_Open)
// open is present
else
// open is not present
if (aStates & State_Assigned)
// assigned is present
else
// assigned is not present
if (aStates & State_Completed)
// completed is present
else
// completed is not present
if (aStates & State_Closed)
// closed is present
else
// closed is not present
...
}
DWORD lState = State_Open | State_Assigned | State_Completed | State_Closed;
// whatever combination you need ...
DoSomething(lState);
在Delphi / Pascal中,最好使用Set
处理此问题,它在内部实现为位掩码,例如:
type
State = (State_Open,State_Assigned,State_Completed,State_Closed);
States = Set of State;
procedure DoSomething(aStates: States);
begin
...
if State_Open in aStates then
// open is present
else
// open is not present
if State_Assigned in aStates then
// assigned is present
else
// assigned is not present
if State_Completed in aStates then
// completed is present
else
// completed is not present
if State_Closed in aStates then
// closed is present
else
// closed is not present
...
end;
var
lState: States;
begin
...
lState := [State_Open,State_Closed];
// whatever combination you need ...
DoSomething(lState);
...
end;