The Beginner's Guide to Using Enum Flags
来源:互联网 发布:linux 网卡驱动位置 编辑:程序博客网 时间:2024/04/30 01:25
原文 Once I was roaming the Visual C++ forum (again), I had to face the fact that bitwise operations, and binary in general, are rarely in beginner's common sense. After having pained my fingers to write a very long answer to that innocent person, it became obvious that I had to share this obfuscated knowledge with the community through this article. This is obviously a beginners article, but if you want to get deeper knowledge about the C/C++ bitwise operators cover, you can read the very complete article, An introduction to bitwise operators by PJ Arends. You can also go into a very complex (but effective) analysis of bit hacking with the article, Bit Twiddling Hacks By Sean Eron Anderson. I'm going to present in the most complete way that I can about what we can do with bitwise operators, flags and all that binary stuff. One of the places where we most find the use for this is certainly when a library provides a set of enumerations and when functions use As we can see, these constants are all powers of 2. To understand why these constants are chosen, we must go in binary representation: Notice that for all of these values, only one We face a problem now. C++ doesn't handle binary directly. We have to use Knowing this, we can use these operators to build some mixes such as the ones presented above. We can see that the bitwise Commonly, such mixes stick to the Say we want to know if the bit of If the OK, now, in practice, here is how you'll often see it: I didn't present the third operator NOT ( I didn't mention the XOR ( Anyway, this operator can be used to easily switch one bit : Now, to perfect your sharpen skills on bits handling, there are few other operators you have to know to be unbeatable: the shift operators. Those can be recognized like this : What a shifting operator is moving the bits of n positions to the right or to the left. The "space" made by the movement is padded with zeros, and the bits pushed over the limit of the memory area are lost. Let's take an example: "But" I hear you say, "what is that for" ?". Well, there are tons of applications which I don't particularly have in mind right now, but trust me, when you need it, be happy it's there. Anyway, two funny uses of such operators I can think of is the division and the multiplication of an integer by 2. Check this: Shifting one bit right, then one bit left gives a different result from the beginning. That's because as we just saw, the "overflown" bits are lost, and the newly inserted ones are set to 0. But let's look closer. This is working anyway because we are working on integers. That is, dividing an odd number will not give a float (we don't care about the remaining 0.5). Here, Now we've seen all the useful operators, just know that all of them have their own assignment operator. Thanks to Randor in the VC++ forum, here is a nice example of what is possible to do with such operators (Credit for the discovery of this little neat trick below goes to Sean Anderson at stanford university). "How is it possible to reverse the bits of an integer, say from 11010001 to 10001011 ?" "Wowwww, how is this working man ?!" Well, here we are then. If you came here to understand these binary operations, I hope that I helped you in your way. If you came to see what was going on here, then don't hesitate to point my mistakes if any, so that I can fix them. The following links are for complimentary knowledge, if you like to go deeper in bitwise handling: This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)The Beginner's Guide to Using Enum Flags
Introduction
The Facts
DWORD
as a flags container. Let's take for that article the example of an enum
which defines some styles:enum { STYLE1 = 1, STYLE2 = 2, STYLE3 = 4, STYLE4 = 8, STYLE5 = 16, STYLE6 = 32, STYLE7 = 64, STYLE8 = 128};
ORenum { STYLE1 = 0x1, STYLE2 = 0x2, STYLE3 = 0x4, STYLE4 = 0x8, STYLE5 = 0x10, STYLE6 = 0x20, STYLE7 = 0x40, STYLE8 = 0x80};
1 -> 0b 00000000 00000000 00000000 00000001 2 -> 0b 00000000 00000000 00000000 00000010 4 -> 0b 00000000 00000000 00000000 00000100 8 -> 0b 00000000 00000000 00000000 00001000 16 -> 0b 00000000 00000000 00000000 00010000 32 -> 0b 00000000 00000000 00000000 00100000 64 -> 0b 00000000 00000000 00000000 01000000 128 -> 0b 00000000 00000000 00000000 10000000
bit
is set at a time, all the others are equal to 0
. You now can see a high level of interest appearing in this, which is that each bit is used as a flag for a functionality (here, each bit represents a style). We can now imagine a way to mix the flags together in one variable, to avoid using as many booleans as we need flags. Consider the following example : 0b 00000000 00000000 00000000 00100101 Flags of Style1, Style3 and Style6 are set
The Main Operators
bitwise operators
instead. There are 3 atomic bitwise operators to know, presented by ascending order of priority : OR (|
), AND (&
) and NOT (~
). Here are their behaviors: x y | x y & x ~ --------- --------- ------- 0 0 0 0 0 0 0 1 0 1 1 0 1 0 1 0 1 0 1 1 0 0 1 1 1 1 1 1
In the case of the instruction STYLE1 | STYLE3 | STYLE6
, we OR
the constants like this: 0b 00000000 00000000 00000000 00000001 <- STYLE1 0b 00000000 00000000 00000000 00000100 <- STYLE3 0b 00000000 00000000 00000000 00100000 <- STYLE6----------------------------------------------- 0b 00000000 00000000 00000000 00100101 <- STYLE1 | STYLE3 | STYLE6
OR
operator is pretty much like the addition (+) operator. However, you have to be very careful if you wished to use + instead or |. The reason is simple: adding 1 + 1
resolves into 0
(plus one carry over1
). You won't see any problem if all the constants are strictly different though, but I won't go deeper as it is a bad practice to use other than bitwise operators when processing binary operations.DWORD in Action
DWORD
type. However, this is not a must as we can do this with any integer types (char
, short
, int
, long
...). A DWORD
is an unsigned 32 bits integer (like those used in the binary representations of this article). Let's imagine the case when we have such a DWORD
constructed in a function, and passed to another in parameter. How can the called function know which bits are set, and which are not? Easy... Follow me!STYLE8
is set in the DWORD
passed in parameter. We must have a mask which we will call the AND
parameter. In practice, the mask is the same as the constant we are about to test, so there's no additional code to create such a mask: DWORD parameter -> 0b 00000000 00000000 00000000 00100101 STYLE8 mask -> 0b 00000000 00000000 00000000 10000000 ---------------------------------------- Bitwise AND -> 0b 00000000 00000000 00000000 00000000 <- 0x00000000 DWORD parameter -> 0b 00000000 00000000 00000000 10100101 STYLE8 mask -> 0b 00000000 00000000 00000000 10000000 ---------------------------------------- Bitwise AND -> 0b 00000000 00000000 00000000 10000000 <- STYLE8
AND
operation returns 0
, then, the bits were not set, otherwise, you get the mask you applied.void SetStyles(DWORD dwStyles) { if ((STYLE1 & dwStyles) == STYLE1) { //Apply style 1 } else if ((STYLE2 & dwStyles) == STYLE2) { //Apply style 2 } else if ((STYLE3 & dwStyles) == STYLE3) { //Apply style 3 } //etc...}
~
) yet. It is generally used when you have a set of bits, in which some are set and some aren't, and where you'd like to remove one of them. The sample of code below exposes how this can be done:void RemoveStyle5 (DWORD& dwStyles) { if ((STYLE5 & dwStyles) == STYLE5) { dwStyles = dwStyles & ~STYLE5; }}
^
) operator yet. The reason why it comes last is for the only reason that this operator is not atomic; that means that we can reproduce its behavior with the other operators presented already:#define XOR(a,b) (((a) & ~(b)) | ((b) & ~(a)))
void SwitchStyle5 (DWORD& dwStyles) { dwStyles = dwStyles ^ STYLE5;}
The Other Operators
<<
(left shifting, follow the arrows), >>
(guess what, this is a right shifting).BYTE dwStyle = STYLE1 | STYLE3 | STYLE6; // [00100101]000 <-dwStyle = dwStyle << 3; // 001[00101000] BYTE dwStyle = STYLE1 | STYLE3 | STYLE6; // 000[00100101] ->dwStyle = dwStyle >> 3; // [00000100]101
char i = 127; // 0b01111111 ( = 127)i = i >> 1; // 0b00111111 ( = 63)i = i << 1; // 0b01111110 ( = 126)
127 / 2
should be 63.5
, so it gives 63
, due to the truncation.63 * 2 = 126
, isn't it what we want ?! :-)dwStyle &= 0x2 --> dwStyle = dwStyle & 0x2dwStyle |= 0x2 --> dwStyle = dwStyle | 0x2dwStyle ^= 0x2 --> dwStyle = dwStyle ^ 0x2dwStyle <<= 0x2 --> dwStyle = dwStyle << 0x2dwStyle >>= 0x2 --> dwStyle = dwStyle >> 0x2
A Funny Example
BYTE b = 139; // 0b10001011b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
DWORD iTmp = 0;BYTE b = 139; // 00000000 00000000 00000000 10001011DWORD c = (b * 0x0802LU); // 00000000 00000100 01011001 00010110c &= 0x22110LU; // 00000000 00000000 00000001 00010000DWORD d = (b * 0x8020LU); // 00000000 01000101 10010001 01100000d &= 0x88440LU; // 00000000 00000000 10000000 01000000iTmp = (c | d); // 00000000 00000000 10000001 01010000iTmp = iTmp * 0x10101LU; // 10000001 11010001 11010001 01010000iTmp >>= 16; // 00000000 00000000 10000001 11010001b = iTmp; // 00000000 00000000 00000000 11010001
Conclusion
Links
License
About the Author
- The Beginner's Guide to Using Enum Flags
- The Beginner's Guide to Broadband and Wireless Internet
- Beginner's guide on using IRC
- A Beginner's Guide to Pointers
- Absolute Beginner's Guide to Project Management
- Beginner's Guide To ASP.NET Cookies
- A Beginner's Guide To btrfs
- A Beginner’s Guide to LibCurl
- The Absolute Beginner’s Guide to Node.js( Node.js 新手指南 (Manuel Weiss))
- Nginx: Beginner’s Guide
- A Beginner's Guide to Creating a MMORPG
- A Beginner's Guide to Creating a MMORPG
- 指针入门指导 -- A Beginner's guide to Pointers
- A beginner's guide to search engine optimization and promotion
- Absolute Beginner's Guide to eBay (4th Edition)
- A Beginner's Guide to Creating a MMORPG
- 转:Beginner's Guide To ASP.NET Cookies
- JavaScript_A Beginner's Guide - Introduction to JavaScript - 09/19/2012
- 阳光明媚的夏日午后
- lucene中建立实时索引的方法
- VC中解析XML CMarkup类的使用
- IE6中a:hover的CSS伪类无效
- Android 增加鼠标支持
- The Beginner's Guide to Using Enum Flags
- VMWare Workstation 安装 VMware Tools
- Java中获取完整的url
- Hackers have got commenced while using the great calculating
- vs2005无法启动,VS 2008无法启动
- squid反向代理
- 在Oracle (CentOS) Linux 5.5 (x32/x64)上安装Oracle 10g r2
- 小型图书馆管理系统
- “遭遇黄牛党”一次系统优化实录