Logical operations in C¶
Since the 1999 version of the standard, C has had a bool
datatype
for representing true and false values. Before then, and sometimes still,
you’ll see people use other types, and the language allows almost any
value to be used to represent truth using the following simple rule.
zero is false
everything else is true
So, the following would print its message.
if (17)
puts("hello, world");
On the other hand, the following would not print anything.
if (0.0)
puts("hello, world");
Sometimes programmers rely on anything not equal to zero being true; for example, you’ll sometimes see tests against zero abbreviated.
if (size != 0)
puts("the thing isn't empty");
/* or, equivalently... */
if (size)
puts("the thing isn't empty");
However, when somebody is explicitly trying to write a value intended
to be true, they will always use 1
, or the special constant
true
, which is just a fancy way of writing 1
when that
would be clearer.
To have access to true
, false
, and the bool
datatype, you must include the <stdbool.h>
header. A bool
variable is more clear than using some other type such as int
when what you mean is to store a truth value, and it is likely smaller
as well. A value of any other type, when stored into a bool
,
will be converted to 0 or 1 according to the truthiness rule above.
The important Boolean operators are all defined at a logical level.
The AND operator is &&
, the OR operator is ||
, and
the NOT operator is !
.
bool a = false;
bool b = true;
bool x = a && b; /* both a AND b */
bool y = a || b; /* either a OR b */
bool z = !a; /* NOT a, the opposite */
There is no logical XOR operator, unless you remember that equality and inequality are Boolean operators.
bool x = a == b; /* a equals b */
bool y = a != b; /* a and b differ, i.e. a XOR b */
There is one important caveat to using ==
and !=
in this way. The operands to &&
, ||
, and !
are converted to bool
, so anything that isn’t 0 will count as
1. That’s obviously not true for ==
and !=
, so some values
that count as both true still don’t count as equal to each other. This
isn’t an issue if you make sure the operands are bool
.
The null pointer compares equal to zero, so it counts as false, and all pointers that could point to a valid object aren’t zero, so they count as true.
int x = 10;
int *px = &x;
int *pn = NULL;
if (px)
puts("px points to something");
if (!pn)
puts("pn is a null pointer");
The logical AND and OR operators are ‘short-circuiting’, meaning
that as soon as it is possible to know what the result will be, they
don’t bother evaluating any further. Note that true || x
is
going to result in true
, regardless of the value of x
.
Similarly, false && y
is going to result in false
,
regardless of y
. This can be used to make a simple kind of
conditional execution, and is most commonly seen protecting tests that
could have undefined behavior otherwise.
/* does px point to an integer storing 10? */
if (px && *px == 10)
puts("px points to something, and that something is 10");
/* looks similar, but has undefined behavior */
if (*px == 10 && px)
If px
is a null pointer, dereferencing it to check whether its
target is 10 has undefined behavior, likely a crash. The first example
checks whether px
is a non-null pointer, and then only if it is
does the dereferencing happen. The second example always triggers that
potentially undefined behavior, and then only if it survives does it
check whether it would have been okay. In Boolean algebra i.e. math,
the order of the operands to AND and OR doesn’t matter—they’re both
commutative operators. In C it can matter, in case evaluating one of
the operands is much slower or not necessarily safe.