Jump to content

Bit Flags Tutorial with Example

- - - - -

This topic has been archived. This means that you cannot reply to this topic.
4 replies to this topic

#1
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,118 posts
C/C++: Bit Flags

What are bit flags?

Bit flags , or bit fields are a great way of storing several boolean values in a single byte (or set of bytes), and are internally represented as binary. In this tutorial we will work with bitwise operators, so if you need to brush up this is what we're using; the following key binary operators.

Here they are:

AND (a & b): Bits that are set in both a and b are set.
OR: (a | b): Bits that are set in either a or b are set.
XOR (a ^ b): Bits that are set in a or b but not both are set.
NOT (~a) : Bits in a are complemented (inversed)

In this lesson we will need to work with an unsigned integer, which can store successfully four bytes of data but for simplicity's sake we will only work with one (a char in this case). Note that one byte = eight bits:
[COLOR=blue]unsigned char[/COLOR] options;
We will need to define the required options and their bits in an enum field, in here the options will be placed at separate bits in the byte. You may note my simple naming convention, we should avoid using anything too fancy here:

enum Options {
    OPT_A   = 0x01,
    OPT_B   = 0x02,
    OPT_C   = 0x04,
    OPT_D   = 0x08,
    OPT_E   = 0x10,
    OPT_F   = 0x20,
};
Each option will now have its own bit flag, what we will need to understand is its binary representation so we can successfully use bitwise operators. The simple conversion from hexadecimal on what we defined is as follows:

 00000000 Meaning       Bin Hex    | Examples
 │││││││└ Preference 1  2^0   1    | Pref 1+2   is Hex 3 is 00000011
 ││││││└─ Preference 2  2^1   2    | Pref 1+8   is Hex 81 is 10000001
 │││││└── Preference 3  2^2   4    | Pref 3,4+6 is Hex 2C is 00101100
 ││││└─── Preference 4  2^3   8    | all Prefs  is Hex FF is 11111111
 │││└──── Preference 5  2^4  10    |
 ││└───── Preference 6  2^5  20    | etc ...
 │└────── Preference 7  2^6  40    |
 └─────── Preference 8  2^7  80    |
So we can apply bitwise operators like this:

Now assume option A is 0x5 and option B is 0x7:
Bitwise OR, A | B = C:
[FONT=Courier New]   00000101 (0x5)
OR 00000011 (0x3)
 = 00000111 (0x7)[/FONT]
As you may notice, both options are now set into C.

Here we will use our other operator:
Bitwise AND, A & B = C
[FONT=Courier New]    00000101 (0x5)
AND 00000011 (0x3)
  = 00000001 (0x1)[/FONT]
A & B = decimal ONE, therefor we can assume B is set as it is non-zero.


This next part I will explain how we may use it in our code, this example shows setting three options (options A, E and F) and then verifying if option F was is set.

//       8*1b            0x1    0x10    0x20  =  0x31
unsigned char options = OPT_A | OPT_E | OPT_F;

//     0x31 & 0x20 = 0x20 > 0 = true
if (options & OPT_F) {
    printf("Option F is set!");
}
That is essentially it. There are many different aspects of the program you can use bit fields for, from dealing with database formats or setting flags for user permissions. It is a simple and clean method of storing key values without wasting variable space.

As people tend to like darned examples instead of just a lecture, I will write something full to include arguments in my example application!

arguments.c:
#include <stdio.h>

//anonymous enum of options, can be done any way you wish
enum {
    OPT_A = 0x01,
    OPT_B = 0x02,
    OPT_C = 0x04,
    OPT_H = 0x08,
};

int main(int argc, char **argv)
{
    //unsigned int, or rather uint32_t = 8*4=32 bits for options if needed, unsigned means last bit is ours to use
    unsigned int opt = 0x0;
    //can do char array for options like '-nodebug'
    char c;

    //extract arguments from argument array.
    while((++argv)[0] && argv[0][0] == '-')
    {
        while((c = *++argv[0]) != 0)
        {
            switch(c) { 
            case 'a':
            //assign option bits to "opt" bit array
                opt |= OPT_A;  break;
            case 'b':
                opt |= OPT_B;  break;
            case 'c':
                opt |= OPT_C;  break;
            case 'h':
                opt |= OPT_H;  break;
            //this will happen if they enter an invalid option:
            default: 
                printf("%s: Unknown option %c", argv[0], c);
                return 1; //break out of application
            }
        }
    }
    
    //apply bitwise AND to check for assignedness a few times
    if(opt & OPT_A)
        printf("Hello World!\n");
   
    if(opt & OPT_B) {
        unsigned int foo;
        foo = 2000;
        printf("Foo has been initialized.\n");
    }
    
    //compare if two flags were specifically set
    if ((opt & (OPT_B | OPT_C)) == (OPT_B | OPT_C)) 
        printf("Flags B and C were set.\n");
    
    if(opt & OPT_H) {
        //print help, may wish to create exit point to stop program from executing
        printf("\tHelp is not implemented yet\n\tAllowable options: [-abch]\n");
        return 0;
    }
  
    //----------------- Some fun extras: ---------------------//
    
    //Reset bitflag completely
    opt = 0;
    
    //Apply bitwise OR to append multiple flags
    opt = (OPT_A | OPT_B | OPT_C);
    
    //Apply bitwise AND+EQUALS to add or remove flags to existing option field
    //Then we apply bitwise NOT (a complement) to remove both flags
    opt &= ~(OPT_A | OPT_B);
    
    //Options A and B are now removed
    
    //Check if BOTH flags are not set
    if ((opt & (OPT_A | OPT_B)) == 0)
        //printf( A and B are not set )

    //check if only one is not set
    if ((opt & OPT_A) == 0)
        //printf( flag A is not set )
        
    //end program
    return 0;
    
}
And that is simply it. You may call the program using any which option you wish, some examples include:
./arguments -a -b
./arguments -abc
./arguments -h
./arguments -z
And that concludes the lesson!

Edited by Alexander, 16 November 2010 - 09:28 PM.
Rewrite with commented code


#2
demonbreath23

demonbreath23

    Newbie

  • Members
  • Pip
  • 3 posts
thanks for this tutorial.

#3
Guest

Guest

    Writes binary right handed and hex left handed

  • Members
  • PipPipPipPipPipPipPipPipPip
  • 3,414 posts
Great job on this.
Posted Image
Root Beer == System Administrator's Beer
Download the new operating system programming kit! (some assembly required)

#4
FireGator

FireGator

    Learning Programmer

  • Members
  • PipPipPip
  • 37 posts
This was the only tutorial that made sense to me, awesome work. +rep

My only question is: Why do we need to use an unsigned char or integer to store the values as you did, is there any special effect/requirement given with one?
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]
>++++++++[<++++>-] <.>+++++++++++

#5
Alexander

Alexander

    It's Science!

  • Moderators
  • 4,118 posts

FireGator said:

Why do we need to use an unsigned char or integer to store the values as you did, is there any special effect/requirement given with one?

Signed data types reserve the leftmost (most significant bit) for signedness, i.e. negative representation with a two's complement, removing that feature allows us to use all bits (although the signed bit is still available to us, we should explicitly show we are using all bits)

EDIT: Finally updated this tutorial a lot, with better examples.

Edited by Alexander, 29 May 2011 - 06:25 PM.

Be sure to read the updated FAQ! || Health is achieved through the same 10,000 steps.
If a suggested code/method fails, informing us is less important than telling us why or what errors occurred.