#047 Hash Define or Const?
Comparing the use of
#define preprocessor macros and
Anyone who has some basic C education, or who has had to pickup and use some existing code, will have encountered preprocessor macros:
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:
constfor defining constants
#pragma oncefor header guards
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
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.
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;
X is really the only value that matters for the program, 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;
The demo.c program demonstrates using
const as an alternative for
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)