CConstants & const

Constants & const

A constant is a value that doesn't change while the program runs. You've already been writing constants without necessarily naming them — the 10 in int x = 10; is a literal constant. This page is about naming constants so your code is easier to read and maintain, and about the two very different tools C gives you for that: the preprocessor's #define and the const keyword.

Literal constants — a quick recap

Literal constants are values written directly into your source code: 42, 3.14, 'A', "hello". They have no name — they're just values sitting in the middle of an expression. The rest of this page covers ways to give a value a name so it can be reused and understood.

#define — the old-school macro constant

#define is a preprocessor directive. Before the compiler ever sees your code, the preprocessor performs a pure text substitution: every occurrence of the defined name is replaced with its replacement text.

C
#include <stdio.h>

#define MAX_STUDENTS 30
#define PI 3.14159

int main(void) {
    int roster[MAX_STUDENTS];
    double area = PI * 5 * 5;

    printf("Roster size: %d\n", MAX_STUDENTS);
    printf("Area: %f\n", area);
    return 0;
}

Because this is textual substitution and happens before compilation, MAX_STUDENTS has no type and no memory address — it simply doesn't exist anymore by the time the compiler runs; the compiler only ever sees 30. This also means a debugger can't show you the value of MAX_STUDENTS by name, and the compiler can't give you type errors if you misuse it.

const — a type-safe, read-only variable

The const qualifier is the more modern approach. It creates an actual, typed variable that the compiler will refuse to let you modify after initialization:

C
#include <stdio.h>

int main(void) {
    const int max_students = 30;
    const double pi = 3.14159;

    printf("Roster size: %d\n", max_students);

    max_students = 35; /* compile-time error: assignment of read-only variable */
    return 0;
}

Because const variables have a real type, the compiler can catch mistakes that #define can't — for example, passing a const int where a function expects a double * produces a proper type warning or error, whereas a careless macro substitution might compile and fail silently at runtime.

const is a 'read-only variable', not a true constant
This is one of the most misunderstood corners of C. `const` does **not** mean "compile-time constant" the way `final` in Java or a literal does in many languages — it means "a variable the compiler won't let *you* modify through this name." Under the hood it's still an ordinary variable stored in memory. In fact, with enough effort (casting away `const` through a pointer), the underlying memory *can* be modified — doing so is undefined behavior, but it demonstrates that `const` is a compiler-enforced promise, not a hardware-level guarantee.
const values are not always valid array sizes
In strict C89, and in some contexts even in later standards, a `const int` is **not** considered a true compile-time constant expression, so it cannot be used to size a fixed array or as a `case` label:

C
const int SIZE = 10;
int arr[SIZE]; /* rejected by strict C89 compilers -- SIZE is not a
                   constant expression, even though it can never change */

#define SIZE2 10
int arr2[SIZE2]; /* always fine -- this is genuinely replaced with 10
                     before the compiler even looks at it */

Modern C (C99 and later) actually allows this particular case because of variable-length arrays, but relying on that is non-portable to strict C89 environments. When you need a value that is guaranteed to work as an array size or case label everywhere, #define or enum are the safer choices.

#define vs const at a glance

Aspect

#define

const

Processed by

Preprocessor (text substitution)

Compiler (real variable)

Has a type

No

Yes

Type checking

None

Full

Visible to a debugger

No

Yes

Valid array size (strict C89)

Yes

No

Occupies memory

No

Usually yes (unless optimized away)

Scope rules

None -- applies everywhere after the #define

Normal C scope rules apply

A third option: enum

C also lets you define a whole group of related named integer constants at once using enum, which gets its own dedicated page later. As a preview:

C
enum Weekday { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY };
/* MONDAY is 0, TUESDAY is 1, and so on, unless you assign values explicitly */

For a single value, #define or const is usually the right tool. For a related family of named integer values, enum tends to produce clearer, more self-documenting code.