Fork me on GitHub

Project Notes

#047 Hash Define or Const?

Comparing the use of #define preprocessor macros and const statements.


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)

Credits and References

About LCK#43 C
Project Source on GitHub Return to the Project Catalog

This page is a web-friendly rendering of my project notes shared in the LittleCodingKata GitHub repository.

LittleCodingKata is my collection of programming exercises, research and code toys broadly spanning things that relate to programming and software development (languages, frameworks and tools).

These range from the trivial to the complex and serious. Many are inspired by existing work and I'll note credits and references where applicable. The focus is quite scattered, as I variously work on things new and important in the moment, or go back to revisit things from the past.

This is primarily a personal collection for my own edification and learning, but anyone who stumbles by is welcome to borrow, steal or reference the work here. And if you spot errors or issues I'd really appreciate some feedback - create an issue, send me an email or even send a pull-request.