Constants: const, #define & constexpr
Some values in a program should never change: the GST rate, the number of subjects in an exam, the days in a week. If you type 0.18 or 60 all over your code, nobody knows what those numbers mean, and changing one means hunting for every copy. Such unexplained numbers are called magic numbers.
C gives you several ways to name a fixed value: const variables, #define macros, and, since C23, constexpr. In this lesson you will learn how to write literal values correctly and when to use each kind of named constant.
Literals: values written directly in code
A literal is a value typed straight into the code, such as 42, 3.5, 'A' or "Pune". Whole-number literals can be written in several bases:
#include <stdio.h>
int main(void)
{
int decimal = 255;
int hex = 0xFF; // base 16: starts with 0x
int octal = 0377; // base 8: starts with 0
int binary = 0b1111'1111; // base 2 (C23)
long long one_crore = 1'00'00'000; // digit separators (C23)
printf("%d %d %d %d\n", decimal, hex, octal, binary);
printf("One crore = %lld\n", one_crore);
int roll_code = 0124; // looks like 124, but the 0 makes it octal
printf("Roll code: %d\n", roll_code);
return 0;
}255 255 255 255 One crore = 10000000 Roll code: 84
All four numbers in the first line are the same value written in different bases. The last line shows a real trap: a leading zero makes a literal octal (base 8), so 0124 means 1×64 + 2×8 + 4 = 84. Never pad decimal numbers with zeros in C code. If a code like a roll number has leading zeros, it is text, not a number.
const: read-only variables
Adding const before a declaration makes the variable read-only. You must give it a value when you declare it, and any later attempt to change it is a compile error.
#include <stdio.h>
int main(void)
{
const double GST_RATE = 0.18;
const int DELIVERY_FEE = 40;
double price = 1299.0;
double total = price + price * GST_RATE + DELIVERY_FEE;
printf("Total: Rs %.2f\n", total);
// GST_RATE = 0.12; // error: assignment of read-only variable 'GST_RATE'
return 0;
}Total: Rs 1572.82
If you remove the // from the commented line, GCC refuses to compile and prints the error shown in the comment. A const variable has a type, follows normal scope rules and can be inspected in a debugger. Its value can even come from a calculation made while the program runs, for example const int today = day_of_month();.
Many programmers write constant names in capital letters so they stand out from normal variables. This is only a convention; the compiler does not care.
#define: text replacement by the preprocessor
A macro made with #define tells the preprocessor to replace a name with some text before the compiler sees the code. It is the oldest way to name constants in C, and you will find it in almost every C program and header file.
#include <stdio.h>
#define MAX_STUDENTS 60
#define PASS_MARK 33
#define SCHOOL_NAME "Green Valley School, Rohtak"
int main(void)
{
int enrolled = 54;
printf("%s\n", SCHOOL_NAME);
printf("Seats left: %d of %d\n", MAX_STUDENTS - enrolled, MAX_STUDENTS);
printf("Pass mark: %d\n", PASS_MARK);
return 0;
}Green Valley School, Rohtak Seats left: 6 of 60 Pass mark: 33
Notice the format: #define NAME replacement, with no = and no semicolon. Because a macro is plain text replacement, it has no type and ignores scope, and careless macros cause surprising bugs. The classic one is a missing pair of brackets:
#include <stdio.h>
#define BOXES 10 + 5 // missing brackets
#define SAFE_BOXES (10 + 5)
int main(void)
{
printf("%d\n", BOXES * 2); // becomes 10 + 5 * 2
printf("%d\n", SAFE_BOXES * 2); // becomes (10 + 5) * 2
return 0;
}20 30
The preprocessor pasted 10 + 5 in place of BOXES, and multiplication happened before addition. Always wrap a macro's value in brackets when it contains an operator. You will learn much more about macros in The Preprocessor & Macros.
constexpr: true compile-time constants (C23)
A const int in C is a read-only variable, not a true constant. That surprises people coming from C++, and it has practical effects: you cannot use a const int as a case label in a switch, and using it as an array size creates a variable-length array, which cannot be initialised with a list of values. C23 fixes this with constexpr. A constexpr object must get its value at compile time, and it can be used anywhere C needs a constant.
#include <stdio.h>
constexpr int SUBJECTS = 5;
constexpr int MAX_MARKS = 100;
constexpr double PASS_PERCENT = 33.0;
int main(void)
{
int marks[SUBJECTS] = {78, 85, 91, 66, 88};
int total = marks[0] + marks[1] + marks[2] + marks[3] + marks[4];
double percent = total * 100.0 / (SUBJECTS * MAX_MARKS);
printf("Total: %d out of %d\n", total, SUBJECTS * MAX_MARKS);
printf("Percentage: %.1f%%\n", percent);
printf("Passed: %d\n", percent >= PASS_PERCENT);
return 0;
}Total: 408 out of 500 Percentage: 81.6% Passed: 1
marks is an array, a row of five int values, which you will study in Arrays. Because SUBJECTS is constexpr, marks is an ordinary fixed-size array and can be initialised with a list of values. With const int SUBJECTS = 5; instead, GCC would complain that a variable-sized object may not be initialised. In C23, constexpr applies only to variables; unlike C++, C has no constexpr functions. %% inside printf prints a single percent sign.
Which one should you use?
| Kind | Has a type | Usable as array size or case label | Notes |
|---|---|---|---|
#define | No, plain text | Yes | Works in every C version; needs careful brackets |
const variable | Yes | No | Value may come from a run-time calculation |
constexpr (C23) | Yes | Yes | Value must be known at compile time |
enum constant | Yes (int) | Yes | The classic typed way to name whole numbers |
For new C23 code, prefer constexpr for fixed numbers, const for values that are worked out at run time but must not change, and #define when you need features only the preprocessor has, or when your code must also build with older compilers. enum constants are covered in Unions, Enums & typedef.
Common mistakes
- A semicolon or
=in a#define.#define RATE = 18;pastes= 18;whereverRATEappears and causes strange errors. - Leading zeros on numbers.
0124is 84, and089is an error because 8 and 9 are not octal digits. - Missing brackets in macro values. Write
#define TOTAL (A + B), not#define TOTAL A + B. - Trying to change a
const. The compiler stops with "assignment of read-only variable". If the value must change, it is not a constant. - Repeating magic numbers. If a number has a meaning, give it a name once and use the name everywhere.
Key points
- Literals can be decimal, hex (
0x), octal (leading0) or, in C23, binary (0b). - C23 digit separators make long numbers readable:
1'00'00'000. constmakes a read-only variable that has a type but is not a compile-time constant in C.#definereplaces text before compilation; use brackets and no semicolon.- C23
constexprcreates typed compile-time constants usable as array sizes and case labels. - Name your fixed values instead of scattering magic numbers through the code.
Exercise
A parking lot in Lucknow charges Rs 30 for the first hour and Rs 20 for each extra hour, with a maximum charge of Rs 150 per day. Write a program that stores these three values as constexpr constants and calculates the fee for a car parked for 5 hours (without using if, since the fee is below the maximum). Print the fee and the maximum.
Show solution
#include <stdio.h>
constexpr int FIRST_HOUR = 30;
constexpr int EXTRA_HOUR = 20;
constexpr int DAILY_MAX = 150;
int main(void)
{
int hours = 5;
int fee = FIRST_HOUR + (hours - 1) * EXTRA_HOUR;
printf("Fee for %d hours: Rs %d\n", hours, fee);
printf("Daily maximum: Rs %d\n", DAILY_MAX);
return 0;
}Fee for 5 hours: Rs 110 Daily maximum: Rs 150
The first hour costs 30 and the other 4 hours cost 4 × 20 = 80, so the fee is 110. If the rates change, you edit three lines at the top instead of searching the whole program.
Quick Quiz
1. What value does the literal 017 have in C?
A leading zero makes the literal octal: 1×8 + 7 = 15.
2. Given #define DOUBLE_IT 5 + 5, what is DOUBLE_IT * 2?
The macro pastes text, so the expression becomes 5 + 5 * 2, which is 15.
3. Which can be used as an array size for an initialised fixed-size array in C23?
constexpr values are compile-time constants; a const int is only a read-only variable in C.
4. What is wrong with #define RATE 18;?
Everything after the name, including the semicolon, is pasted wherever RATE appears.
Learn C with a mentor
Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
