C Constants
A constant in C is a value that your program should not change while it runs. Constants matter because they give important fixed values clear names, prevent accidental reassignment, and make calculations easier to understand. C has several kinds of constants, including literal values such as 10, typed const objects, and preprocessor names created with #define.
Overview: How Constants Work
The word constant can mean two related things in C. A literal constant is a value written directly in the source code, such as 7, 3.14, 'A', or "C". A named constant gives a useful name to a fixed value, such as days_in_week or MAX_SCORE.
The most type-aware way to make a named constant is to declare an object with const. For example, const int max_attempts = 3; creates an int object whose value may be read but not modified through that name. The compiler still knows the type, so it can check assignments, conversions, function calls, and printf format choices.
Internally, a const local variable is still an object with a type and storage, much like an ordinary variable. The important difference is that the compiler rejects code that tries to assign a new value to it after initialization. Depending on optimization, the compiler may also replace reads of a simple constant with the value itself, but you should write code for meaning first and let the compiler optimize later.
C also has preprocessor constants made with #define. A macro name is replaced by its text before normal C compilation begins. This is useful for simple compile-time configuration, but it has no type of its own. Because of that, prefer const for ordinary typed values inside C code, and use #define when you specifically need preprocessor behavior.
Syntax
const int max_count = 10;
#define MAX_COUNT 10
| Part | Meaning |
|---|---|
const |
A type qualifier that says the object should not be modified after initialization. |
int |
The data type. Other choices include double, char, and many more. |
max_count |
The identifier used later in expressions. |
10 |
The initializer or replacement text that provides the constant value. |
#define |
A preprocessor directive. It is handled before the compiler parses normal C statements. |
A const object should normally be initialized where it is declared. A #define directive does not use an equals sign and does not end with a semicolon.
Examples
Using const for a Named Value
#include <stdio.h>
int main(void)
{
const int days_in_week = 7;
const int hours_per_day = 24;
int total_hours = days_in_week * hours_per_day;
printf("Days: %d\n", days_in_week);
printf("Hours in a week: %d\n", total_hours);
return 0;
}
Output:
Days: 7
Hours in a week: 168
The constants make the calculation read like the idea it represents. If someone accidentally writes days_in_week = 8; later, the compiler reports an error instead of silently changing the meaning of the program.
Choosing Literal Types with Suffixes
#include <stdio.h>
int main(void)
{
const int students = 18;
const double average_score = 87.5;
const char grade = 'B';
printf("Students: %d\n", students);
printf("Average: %.1f\n", average_score);
printf("Grade: %c\n", grade);
printf("Unsigned limit example: %u\n", 4000000000U);
return 0;
}
Output:
Students: 18
Average: 87.5
Grade: B
Unsigned limit example: 4000000000
Literal constants have types. The literal 18 is an integer constant, 87.5 is a floating-point constant of type double, 'B' is a character constant, and 4000000000U is unsigned because of the U suffix. These types affect conversions and which printf format specifier is correct.
Using #define for a Preprocessor Constant
#include <stdio.h>
#define MAX_SCORE 100
#define PASSING_SCORE 60
int main(void)
{
int score = 86;
printf("Score: %d out of %d\n", score, MAX_SCORE);
printf("Passing score: %d\n", PASSING_SCORE);
printf("Passed: %s\n", score >= PASSING_SCORE ? "yes" : "no");
return 0;
}
Output:
Score: 86 out of 100
Passing score: 60
Passed: yes
Before compilation, the preprocessor replaces MAX_SCORE with 100 and PASSING_SCORE with 60. This style is common in C, especially for constants that are shared across files through header files.
Constants in a Real Calculation
#include <stdio.h>
int main(void)
{
const double sales_tax_rate = 0.0825;
const double item_price = 19.99;
const int quantity = 3;
double subtotal = item_price * quantity;
double tax = subtotal * sales_tax_rate;
double total = subtotal + tax;
printf("Subtotal: $%.2f\n", subtotal);
printf("Tax: $%.2f\n", tax);
printf("Total: $%.2f\n", total);
return 0;
}
Output:
Subtotal: $59.97
Tax: $4.95
Total: $64.92
The tax rate and item price are fixed inputs for this calculation, while subtotal, tax, and total are results. Naming the fixed inputs avoids unexplained numbers like 0.0825 appearing in the middle of formulas.
How It Works Step by Step
- The compiler sees a declaration such as
const double item_price = 19.99;and records both the type and theconstqualifier. - The initializer is checked against the type. If needed, C converts the initializer to the declared type.
- When the program runs, storage is made available for the object. For a local
const, this is commonly part of the function’s stack frame, although optimized code may avoid a separate memory load. - Expressions can read the constant’s value just like they read a normal variable.
- An assignment that tries to modify the constant through its name is rejected by the compiler.
- For
#define, the replacement happens earlier: the preprocessor substitutes text, then the compiler sees the resulting C program.
This distinction explains a major difference: const int limit = 10; has a C type and obeys scope rules, while #define LIMIT 10 is a textual replacement that exists only during preprocessing.
Common Mistakes
Trying to Reassign a const Object
Code such as max_users = 20; is invalid after max_users has been declared with const. If the value is meant to change, it should be an ordinary variable. If it is fixed, calculate with it without assigning to it again.
#include <stdio.h>
int main(void)
{
const int max_users = 10;
int current_users = 7;
int open_slots = max_users - current_users;
printf("Open slots: %d\n", open_slots);
return 0;
}
Output:
Open slots: 3
Putting a Semicolon in a #define
A macro definition such as #define LIMIT 10; includes the semicolon in the replacement text. That can produce confusing syntax errors later. Leave the semicolon off the directive and use semicolons only in normal C statements.
#include <stdio.h>
#define LIMIT 10
int main(void)
{
int value = LIMIT;
printf("Limit: %d\n", value);
return 0;
}
Output:
Limit: 10
Using Magic Numbers Instead of Names
A magic number is an unnamed literal whose meaning is not obvious. The number 60 might mean seconds in a minute, minutes in an hour, or a passing score. A named constant records the meaning directly in the code.
#include <stdio.h>
int main(void)
{
const int seconds_per_minute = 60;
const int minutes = 5;
int seconds = minutes * seconds_per_minute;
printf("Seconds: %d\n", seconds);
return 0;
}
Output:
Seconds: 300
Best Practices
- Use
constfor fixed typed values inside functions and modules. - Initialize
constobjects when you declare them. - Use clear names that explain the value’s meaning, such as
seconds_per_minuteinstead ofspm. - Use uppercase names for simple macro constants, such as
MAX_SCORE, because that convention warns readers that preprocessing is involved. - Avoid magic numbers in important formulas. Name values that carry business rules, limits, sizes, rates, or units.
- Match literal suffixes and format specifiers to the actual type, such as
Uwith%ufor an unsigned integer. - Prefer
constover#defineunless you need a value during preprocessing or in a context where a macro is the established C pattern. - Compile with warnings enabled so the compiler can catch suspicious conversions, unused constants, and format mismatches.
Practice Exercises
- Write a program that declares
const int minutes_per_hour = 60;and prints how many minutes are in8hours. - Create constants for a rectangle’s width and height, calculate its area and perimeter, and print both results.
- Write a small program with
#define MAX_LOGIN_ATTEMPTS 3. Store a user’s failed attempts in a variable and print how many attempts remain.
Summary
- Constants represent values that should not change while the program runs.
- Literal constants are values written directly in source code, such as
42,2.5,'X', and"text". constcreates a typed object that can be read but not reassigned through its name.#definecreates a preprocessor replacement, not a typed C variable.- Named constants make code clearer, safer, and easier to maintain.
- Good C code uses constants for fixed limits, units, rates, and configuration values instead of scattering unexplained numbers through formulas.
