#026 Hash Define or Const?
Comparing the use of #define
preprocessor macros and const
statements.
Notes
Anyone who has some basic C education, or who has had to pickup and use some existing code, will have encountered preprocessor macros: #define ...
.
So much so, there’s a tendency to use them indiscriminately.
The article How and Why to Avoid Preprocessor Macros provides some sage advice.
I want to avoid making the mistake of replacing one set of “rules” for another. Instead, I think it is useful to think in terms of alternatives i.e. when considering a preprocessor macro, first consider whether an alternative may be a better choice:
const
for defining constants#pragma once
for header guards
Preprocessor Macros
Preprocessor macros are commonly used to define mnemonics in C code.
But they can lead to much trouble, especially when one forgets they are really performing test substitution.
Here’s a classic error:
#define A 1
#define B A + 1
#define C 3
#define X B * C
What is X
? If you read those statements as expressions, the answer of 6 seems obvious, but that’s wrong.
X
is a macro that is expanded to 1 + 1 * 3
and so yields an expression that evaluates to 4.
This can be fixed with liberal deployment of parentheses:
#define A (1)
#define B (A + 1)
#define C (3)
#define X (B * C)
X
now expands to (((1) + 1) * (3))
which evaluates to 6 as is probably intended.
Parentheses are almost always a good idea if you must put numbers and expressions in macros. Better safe than sorry.
Using Constants
Constants provide type safety and also behave as statements (which may better match their actual usage).
const int A = 1;
const int B = A + 1;
const int C = 3;
const int X = B * C;
If X
is really the only value that matters for the program, and A
, B
, and C
are really just inline documentation of how X
is derived,
then one might finesse the implementation to use a mix of macros and constants:
#define A (1)
#define B (A + 1)
#define C (3)
const int X = B * C;
Demo Program
The demo.c program demonstrates using const
as an alternative for #define
macros:
r$ make && ./demo
gcc -g -Wall -O3 demo.c -o demo
The power of parentheses...
X = B * C = A + 1 * 3 = 1 + 1 * 3 = 4 (wrong!)
X2 = B2 * C = (A + 1) * 3 = (1 + 1) * 3 = 6 (correct)
Using const overcomes the issues and with extra type safety...
X3 = B3 * C3 = (A3 + 1) * 3 = (1 + 1) * 3 = 6 (correct)