Of Bitfields and Booleans
Bit twiddling is an important skill that any programmer should have. It allows you to manipulate the smallest entity in computer memory: a single bit. You can do power-of-two arithmetics by bit shifting, and you can use the XOR bit operation for encryption. But much easier than that, a single bit may represent a flag; it is either set or clear.
Modern C includes a bool
type and I absolutely love it. I use it especially
as return type for functions to indicate success (true
) or failure (false
),
where classic C would use int
(zero for success, versus minus one for
failure).
As a practical implementation, a bool
typically compiles to either a
32-bit or an 8-bit integer.
Since like forever, I have always coded bit flags in C using #define
,
like so:
// window flags
#define WF_ACTIVE 1
#define WF_BORDER 2
#define WF_HIDDEN 4
#define WF_FOCUS 8
#define WF_NEED_REDRAW 0x10
#define WF_SOMETHING 0x20
...
if (window->flags & WF_HIDDEN) {
return;
}
if (window->flags & WF_FOCUS) {
window_redraw(window);
}
A flag is a single bit, and therefore the naturally right thing to do
is to (manually) pack multiple flags together in a single integer.
I’m not using bool
in structures when I don’t have to; this saves a couple
of bytes of memory. It may not seem like much, but every little bit helps
(pun intended).
Defines are macros, not very elegant, so we might also use enum
:
typedef enum {
WF_NONE,
WF_ACTIVE = 1,
WF_BORDER = 2,
WF_HIDDEN = 4,
WF_FOCUS = 8,
WF_NEED_REDRAW = 0x10
} WindowFlag;
The nice thing about enum
is that the flags are now typed. This is entirely
theoretical in this case however, because these symbols will only ever be
used as int
. There is something about using enum
for bit flags that
doesn’t quite mesh with me; if a WindowFlag
is a strong type that is a
single bit, then how can they possibly be combined into an integer flags
field? Anyway, on to the next point.
Now comes along a clever guy (namely, me!) who says, “in modern C you can use bitfields, which is much nicer”.
typedef struct {
int is_active:1;
int has_border:1;
int is_hidden:1;
int has_focus:1;
int need_redraw:1;
// and some other members here
// that don't matter for this post
} Window;
Now we can write our code like this:
if (window->is_hidden) {
return;
}
if (window->has_focus) {
redraw_focus(window);
}
Indeed, big improvement. Kudos!
But then there was something fishy going on. The program seemed to misbehave. A glitch? What was happening here?
void window_set_focus(Window* w, bool focus) {
if (w->has_focus != focus) {
window_focus_changed(w);
}
w->has_focus = focus;
}
When you read this code, you expect that the focus_changed
event only occurs
when the focus is different; you can set focus and the event should occur
if it didn’t have focus before. If the window already had focus, the event
should not occur.
NOT SO. It doesn’t work.
The problem with this little snippet is the comparison between the bitfield and a boolean … I was ready to scream “compiler bug!”, but the sad fact is that a bitfield of one is not the same thing as a boolean in the C programming language.
A bitfield of one is not the same thing as a boolean.
The debugger reveals that bitfield has_focus
equals (int)-1
, while
bool focus
equals 1
.
Both are compiled down to 32-bit integers, and both are used here as
truth value. But they are not the same. I feel like the computer is boldly
telling us "TRUE is not equal to True"
.
Ugh. I call it a design flaw, but it’s clear that the language works as
designed; the bitfield is a signed integer consisting of exactly one bit,
which is indeed (int)-1
. The bool
on the righthand side gets an
implicit type conversion to (int)1
.
The bool
in C is a broken type because you can not use it in boolean
operations with integers. What happens instead is that the boolean operation
gets silently upgraded to an integer operation. It’s just … wrong.
You get no compiler warning, not even with -wconversion
enabled.
I guess I should have known. C always had a reputation for doing implicit type conversions.