Using enumerations in C
With the C programming language, you have many options to arrange data in a program. For example, if you wanted to write a simple chess game, you might use bit fields and bit masks to store both the type of piece and its color, for any square on the board:
typedef unsigned char square_t; /* 1 byte */
#define EMPTY 0
#define PAWN 1
#define ROOK 2
#define KNIGHT 3
#define BISHOP 4
#define QUEEN 5
#define KING 6
#define BLACK 8
#define WHITE 0
#define PIECE 7
This makes the assumption that only three bits are needed to store the values 0 to 6, and a single bit (8) to store the color as either black or white. Using a bit mask of 7 (binary 111
) lets the program separate the value of the piece from its color. Such an implementation makes effective use of how computers store data as bits and bytes, but at the expense of making the program more difficult for human programmers to understand.
Let the computer do the work
A more readable implementation leverages the enumeration feature in the C language. With enumeration, we leave the hard work to the computer. The compiler can figure out how to store the data most efficiently, and what values to assign. The programmer is left with names for values, which makes the source code more readable.
In C, you use the enum
keyword to set up an enumeration. For example, if you needed to track either on or off values in a single variable, you could do it with enum
like this:
enum {off, on} power;
power = on;
It doesn’t matter to the programmer what the values are for on
or off
. Instead, the programmer only needs to know the names for these values. This makes the code more readable, such as a test if the power is turned on:
if (power == on) {
puts("yes, it's ready");
}
else {
puts("no, not ready");
}
Updating the chess board with ‘enum’
With enumeration, we can update the chess board program to use enum
instead of bit fields and bit masks. This makes the program both shorter and easier to read.
The program was a simple representation of an 8×8 chess board, and defined two pieces on the board: a black rook and a white pawn. To represent the different pieces that a player might put on a square, we first need to define an enumeration of those pieces:
enum { empty, pawn, rook, knight, bishop, queen, king } piece;
With enumeration, it doesn’t matter what the values are for each piece. We’ll let the compiler do the “heavy lifting” for us, and assign the proper values for each entry in the enumerated list. In practice, enumerations usually start at zero, so empty
is probably 0, pawn
is 1, and so on. But the important part is that we don’t need to know the values, because we can refer to each piece by its name: king
and queen
and so on.
Similarly, we’ll need to define an enumeration of the two possible colors:
enum { black, white } color;
And to store both the piece and its color in one square on the chess board, we can use a structure like this:
typedef struct {
enum { empty, pawn, rook, knight, bishop, queen, king } piece;
enum { black, white } color;
} square_t;
Putting it all together
Let’s combine everything to define an 8×8 chess board with the two pieces on it. We can use the enumerations to store the piece and its color in each square on the board:
#include <stdio.h>
typedef struct {
enum { empty, pawn, rook, knight, bishop, queen, king } piece;
enum { black, white } color;
} square_t;
int main()
{
square_t board[8][8]; /* 0-7 is 8 squares, total is 8x8=64 squares */
board[0][0].piece = rook;
board[0][0].color = black;
board[6][0].piece = pawn;
board[6][0].color = white;
printf("the rook at [0][0] is %s\n",
( board[0][0].color == black ? "black" : "white" ) );
printf("the pawn at [6][0] is %s\n",
( board[6][0].color == black ? "black" : "white" ) );
return 0;
}
If we compile and run this program, we’ll see that the rook is black and the pawn is white:
$ gcc -Wall -o chess chess.c
$ ./chess
the rook at [0][0] is black
the pawn at [6][0] is white
Using enumerations with enum
makes it easy to store values in a program, yet keep the code easy to read. Enumerations are a good “tool” to add to your programmer’s “tool kit,” alongside structures, bit fields, and bit masks. You’ll find that the right tool at the right time can turn a difficult job into an easy one.