C Preprocessor Directives – #define, #if, #ifdef, #ifndef with Examples

C preprocessor directives are instructions that run before compilation. The preprocessor reads your source file, processes every line that starts with #, and hands the result to the compiler. Understanding them well means smaller code, safer constants, conditional compilation for different platforms, and header files that include safely more than once.

Object-Like Macros — #define for Constants

The simplest use of #define is naming a constant. The preprocessor replaces every occurrence of the macro name with its value before the compiler sees the code:

#include <stdio.h>

#define PI       3.14159
#define MAX_SIZE 100
#define GREETING "Hello, C!"

int main(void)
{
    printf("PI        = %.5f\n", PI);
    printf("MAX_SIZE  = %d\n",   MAX_SIZE);
    printf("GREETING  = %s\n",   GREETING);
    return 0;
}
PI        = 3.14159
MAX_SIZE  = 100
GREETING  = Hello, C!

No semicolon after #define. No type. The preprocessor performs text substitution — it does not know about types, scope, or runtime values. This is why const variables are preferred in C99 and later for typed constants, but #define remains the standard for sizes, flags, and string literals used across files.

Function-Like Macros — #define with Parameters

Macros can take arguments, behaving like inline functions but without a function call overhead. Always wrap every argument and the whole expression in parentheses to avoid operator-precedence bugs:

#include <stdio.h>

#define SQUARE(x)  ((x) * (x))
#define MAX(a, b)  ((a) > (b) ? (a) : (b))
#define ABS(x)     ((x) < 0 ? -(x) : (x))

int main(void)
{
    printf("SQUARE(5)     = %d\n", SQUARE(5));
    printf("SQUARE(2+3)   = %d\n", SQUARE(2 + 3));
    printf("MAX(10, 20)   = %d\n", MAX(10, 20));
    printf("ABS(-7)       = %d\n", ABS(-7));
    return 0;
}
SQUARE(5)     = 25
SQUARE(2+3)   = 25
MAX(10, 20)   = 20
ABS(-7)       = 7

Why all those parentheses? Without them, SQUARE(2+3) would expand to 2+3 * 2+3 = 11, not 25. With ((x)*(x)) it correctly becomes ((2+3)*(2+3)) = 25. This is the most common macro bug — always parenthesise.

#if, #elif, #else, #endif — Conditional Compilation

#if evaluates a constant integer expression at compile time and includes or excludes blocks of code. This is the standard way to write code that compiles differently for different configurations:

#include <stdio.h>

#define VERSION 2

int main(void)
{
#if VERSION == 1
    printf("Version 1 — legacy build\n");
#elif VERSION == 2
    printf("Version 2 — current stable\n");
#else
    printf("Unknown version\n");
#endif
    return 0;
}
Version 2 — current stable

The compiler only sees the branch that matches — the other branches are stripped out entirely before compilation. #elif works like else if; you can chain as many as needed. Every #if must end with #endif.

#ifdef and #ifndef — Test Whether a Macro Is Defined

#ifdef NAME is true if NAME has been defined (even with an empty body). #ifndef NAME is true if it has not been defined. This is the standard way to enable debug logging or toggle features at compile time:

#include <stdio.h>

#define DEBUG

int main(void)
{
#ifdef DEBUG
    printf("DEBUG mode: extra logging enabled\n");
#endif

#ifndef RELEASE
    printf("Not a release build\n");
#endif

    printf("Program running\n");
    return 0;
}
DEBUG mode: extra logging enabled
Not a release build
Program running

To switch off DEBUG, just remove or comment out the #define DEBUG line — or pass -UDEBUG to gcc. To define it from the command line without touching the source: gcc -DDEBUG myfile.c.

Include Guards — Preventing Double Inclusion

Every header file should be wrapped in an include guard so it is safe to include more than once (which happens easily when multiple source files include the same header):

/* mymath.h */
#ifndef MYMATH_H
#define MYMATH_H

#define SQUARE(x) ((x) * (x))
#define CUBE(x)   ((x) * (x) * (x))

#endif /* MYMATH_H */
/* main.c */
#include <stdio.h>
#include "mymath.h"
#include "mymath.h"  /* second include — guard prevents redefinition */

int main(void)
{
    printf("SQUARE(4) = %d\n", SQUARE(4));
    printf("CUBE(3)   = %d\n", CUBE(3));
    return 0;
}
SQUARE(4) = 16
CUBE(3)   = 27

On the first inclusion, MYMATH_H is not yet defined, so the body is processed and MYMATH_H gets defined. On the second inclusion, MYMATH_H is already defined, so the entire body is skipped. No redefinition errors.

Preprocessor Directives — Quick Reference

Directive Meaning
#define NAME value Define an object-like macro
#define NAME(x) expr Define a function-like macro
#undef NAME Remove a macro definition
#include <file> Include system header
#include "file" Include local header
#if expr Conditional: constant integer expression
#ifdef NAME Conditional: NAME is defined
#ifndef NAME Conditional: NAME is NOT defined
#elif expr Else-if branch
#else Else branch
#endif End conditional block
#pragma once Non-standard but widely supported alternative to include guards
#error "message" Stop compilation with a message

Common Pitfalls

Mistake Example Fix
Missing parentheses in macro #define SQ(x) x*xSQ(2+3) = 11 #define SQ(x) ((x)*(x))
Semicolon after #define #define N 10;int arr[N]; becomes int arr[10;]; Never put ; after #define
Side effects in macro arguments SQUARE(i++) expands to ((i++)*(i++)) — increments twice Use inline functions or pass a variable, not an expression with side effects
Forgetting #endif Missing #endif causes “unterminated #if” error Every #if / #ifdef needs exactly one #endif

How to Compile

gcc -ansi -Wall -Wextra -o myprogram myprogram.c
# Pass a macro definition from the command line:
gcc -ansi -Wall -Wextra -DDEBUG -o myprogram myprogram.c

Related C Programs

📖 Chapter 4 of The C Programming Language by K&R (Amazon.in) · Amazon.com covers the C preprocessor in full — macros, file inclusion, and conditional compilation.

Preparing for a C interview? Practice with the C Programming Quiz — 150+ questions, free on Android.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>