Some bit twiddling examples

The following example programs put together the bit operations we have explored in order to maintain multiple Boolean flags within larger values.

Peanut butter and jelly

This example asks about the user’s preferences for whether a sandwich should have peanut butter, jelly, and bread in it. Rather than tracking three bools, three separate masks are defined using macro constants, and those masks are used to keep track of the flags within one integer.

#include <stdio.h>

#define PEANUT_BUTTER (1 << 0)
#define JELLY         (1 << 1)
#define BREAD         (1 << 2)

int main()
{
    int options = BREAD;
    char c;

    printf("Do you want peanut butter? [yn] ");
    scanf(" %c", &c);
    if (c == 'y' || c == 'Y')
        options |= PEANUT_BUTTER;

    printf("Do you want jelly? [yn] ");
    scanf(" %c", &c);
    if (c == 'y' || c == 'Y')
        options |= JELLY;

    printf("Have you changed your mind about the peanut butter? [yn] ");
    scanf(" %c", &c);
    if (c == 'y' || c == 'Y')
        options ^= PEANUT_BUTTER;

    printf("Would you like to remove the bread? [yn] ");
    scanf(" %c", &c);
    if (c == 'y' || c == 'Y')
        options &= ~BREAD;

    if (options & BREAD)
        puts("Here's a slice of bread.");
    if (options & PEANUT_BUTTER)
        puts("Here's some peanut butter.");
    if (options & JELLY)
        puts("Here's some jelly.");
    if (options & BREAD)
        puts("Here's a slice of bread.");

    if (!(options & BREAD))
        puts("I'm not sure how that will work with no bread, but...");

    puts("Enjoy your sandwich!");
}

Numbers

Whereas the PB&J example used named, hardcoded masks to handle predefined flags, this example keeps track of a set of numbers by setting the bit corresponding to each number seen. The mask is generated dynamically using a shift.

#include <stdio.h>
#include <stdint.h>

int main()
{
    uint8_t seen = 0;
    int i;

    puts("Give me some numbers. When you type a number greater than 7, I will quit.");
    for (;;) {
        unsigned x;
        scanf("%u", &x);
        if (x > 7)
            break;

        seen |= 1 << x;
    }

    for (i = 0; i < 8; ++i) {
        if (seen & (1 << i))
            printf("You said %d at some point.\n", i);
    }
}

The <stdint.h> header provides some type aliases for integers of a particular width, which are invaluable for making portable programs. In this case, the fields are kept in a uint8_t, read as ‘unsigned integer 8-bit type’. Unsigned integers are generally better for bitsets such as this, because if you use right shifting there is no complication with the sign bit. We need 8 bits to track the numbers 0–7. You can keep track of a 26-letter alphabet in a uint32_t, or the positions on a chessboard in a uint64_t.

These basic bit set/clear/toggle/test patterns can also be used in an array of integers to make bitsets of arbitrary length.

You have attempted of activities on this page