C Constants
By Flavio Copes
Learn how to work with constants in C using the const keyword or a #define directive, plus the convention of naming constants in uppercase for readability.
In C you have two ways to define a constant: the const keyword, and the #define preprocessor directive. Let’s see both, and when to pick one over the other.
In the last post I introduced variables in C. Constants are values that, once set, cannot change while the program runs. Think of the number of months in a year, or the maximum number of players in a game. Making them constant documents your intent, and lets the compiler stop anyone from changing them by accident.
Using const
A constant is declared similarly to variables, except it is prepended with the const keyword, and you always need to specify a value.
Like this:
const int age = 37;
This is perfectly valid C, although it is common to declare constants uppercase, like this:
const int AGE = 37;
It’s just a convention, but one that can greatly help you while reading or writing a C program as it improves readability. Uppercase name means constant, lowercase name means variable.
A constant name follows the same rules for variable names: it can contain any uppercase or lowercase letter, can contain digits and the underscore character, but it can’t start with a digit. AGE and Age10 are valid names, 1AGE is not.
The compiler enforces the promise. If you later write:
AGE = 38;
the program doesn’t compile. With clang you get error: cannot assign to variable 'AGE' with const-qualified type 'const int'. This is the whole point: a bug that would silently change a value becomes a compile error you fix in seconds.
The fix, when you hit this error on purpose, is to admit the value changes and declare it as a normal variable instead.
Using #define
Another way to define constants is by using this syntax:
#define AGE 37
In this case, you don’t need to add a type, you don’t need the = equal sign, and you omit the semicolon at the end.
The C compiler will infer the type from the value specified, at compile time.
What’s the difference?
#define is handled by the preprocessor, which runs before the compiler. It performs a text substitution: every occurrence of AGE in the code becomes 37 before compilation even starts.
That has consequences. A #define has no type, so the compiler can’t check you’re using it correctly. It also ignores scope: it applies from the point of definition to the end of the file, even inside functions and blocks.
A const instead is a typed value, and it follows the normal scoping rules. Declare it inside a function, and it exists only there.
My advice is to prefer const for typed values in your code, and reserve #define for cases where the preprocessor is what you actually want, like configuration flags.
C23: constexpr
C23 adds constexpr for object definitions. The initializer must be a constant expression, the object is implicitly const, and you can use it in other constant expressions:
constexpr int MAX = 100;
int values[MAX];
If you still target pre-C23 compilers, stick with const (and #define when you need a preprocessor constant). When you can use C23, constexpr is the tighter tool for values that must be known at compile time.
Want me to talk about your product? You can sponsor this site.