CMacros (#define)

Macros (#define)

An object-like macro is the simplest form of preprocessor substitution: give a name to a piece of text with #define, and every later occurrence of that name in the source is replaced, character for character, before compilation even starts.

C
#include <stdio.h>

#define PI 3.14159

int main(void)
{
    double radius = 2.0;
    double area = PI * radius * radius;

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

After preprocessing, every PI in the file is gone — replaced by the literal text 3.14159. The compiler never sees a symbol called PI at all; it only ever sees the number.

It Is Text Substitution, Not a Variable

This is the single most important thing to internalize about macros: #define does not create a typed, scoped constant the way a const int or a variable declaration does. It creates a raw text replacement rule that the preprocessor applies blindly, everywhere in the file, with no awareness of C syntax, types, or scope.

Macros are not type-checked and not scoped
Because a macro is pure text substitution, the compiler cannot catch type mistakes involving it, and a macro defined at the top of a file applies everywhere below it — inside every function, every block, regardless of what you might expect from normal variable scoping rules.

C
#define PI 3.14159

int main(void)
{
    int PI = 5;   /* Expands to: int 3.14159 = 5;  -- syntax error! */
    return 0;
}

That snippet fails to compile with a confusing error pointing at 3.14159, not at PI — because by the time the compiler sees the code, PI no longer exists as a name. This is a classic beginner gotcha: using a common word as a macro name can silently clash with an unrelated identifier used somewhere far away in the code (including inside library headers you include).

The ALL_CAPS Naming Convention

Because macros are invisible to the type system and can be expanded anywhere, the C community adopted a strong convention: write macro names in ALL_CAPS. Seeing MAX_BUFFER_SIZE or PI in all caps is an instant visual signal to any reader that this identifier is a preprocessor substitution, not a real variable or function — so they know to think about it differently (no scope, no type, pure text).

  • #define MAX_USERS 100 — a macro constant, all caps

  • int maxUsers = 100; — a real, typed, scoped variable, not all caps

  • Mixing the two styles up is a common source of confusion in code review

Prefer const or enum for constants when you can
In modern C, a `static const int` or an `enum` value often makes a better constant than a macro: it has a real type, respects scope, and shows up in a debugger. Macros still shine for things that genuinely must be pure text (like conditional compilation flags), which later pages cover.
Removing a Macro With #undef

A macro definition stays active for the rest of the file (or until the end of the translation unit) unless you explicitly remove it with #undef. This is occasionally useful when a header defines a macro name you need to reuse for something else afterward.

C
#define BUFFER_SIZE 256

/* ... code that uses BUFFER_SIZE ... */

#undef BUFFER_SIZE
#define BUFFER_SIZE 512   /* redefine safely after #undef */
Note
Redefining a macro to a *different* value without first calling `#undef` is technically an error under the standard (though many compilers only warn). Get in the habit of `#undef`-ing before redefining if there is any chance the macro is already set.