C Mastery / The Preprocessor: Conditionals, Feature Detection, Pragmas
Part 1 — The Core Language

The Preprocessor: Conditionals, Feature Detection, Pragmas

This chapter covers conditional compilation (#if, #ifdef, etc.), feature detection, include guards, and #pragma. These are the preprocessor's tools for portability and configuration.

Why This Matters

Conditional compilation lets one codebase support multiple platforms, standards, and build configurations. Include guards prevent duplicate declarations. Feature detection with predefined macros is the standard way to write portable C.

Prerequisites

Core Concept

Conditional directives

#if expression        /* integer constant expression */
#ifdef NAME           /* true if NAME is defined */
#ifndef NAME          /* true if NAME is not defined */
#elif expression
#else
#endif

The #if expression is evaluated by the preprocessor and must be an integer constant expression (macros are expanded first; undefined identifiers are replaced by 0).

Include guards

#ifndef MY_HEADER_H
#define MY_HEADER_H

/* header contents */

#endif

This ensures the header is included at most once per translation unit.

Feature detection

Predefined macros let you detect the compiler and standard:

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
/* C11 or later */
#endif

__STDC_VERSION__ is 201112L for C11, 201710L for C17, 202311L for C23 (preliminary). Compiler-specific macros (__GNUC__, __clang__, _MSC_VER) identify the implementation.

#pragma

#pragma is an implementation-defined directive. Some pragmas are standardized (e.g., #pragma once is widely supported but not ISO C; #pragma STDC ... is the standard prefix). Pragmas control compiler behavior (packing, warnings, optimization).

Syntax

#if expr
#ifdef NAME
#ifndef NAME
#elif expr
#else
#endif
#pragma once
#pragma STDC ...

Examples

Include guard

#ifndef POINT_H
#define POINT_H

struct Point { int x; int y; };

#endif

Feature detection

#include <stdio.h>

int main(void)
{
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
    puts("C11 or later");
#else
    puts("pre-C11");
#endif
    return 0;
}

Compiler-specific code

#if defined(__GNUC__)
#  define UNUSED __attribute__((unused))
#elif defined(_MSC_VER)
#  define UNUSED
#else
#  define UNUSED
#endif

How It Works

The preprocessor evaluates the controlling expression (after macro expansion) and includes or excludes the corresponding block. Excluded code is not compiled. This happens before the compiler proper sees the translation unit.

Variations

#if vs. #ifdef

#ifdef NAME checks only whether NAME is defined (regardless of value). #if NAME checks the *value* (with undefined names evaluating to 0). Use defined(NAME) inside #if for combined checks:

#if defined(A) && !defined(B)

#error and #warning

#error "unsupported platform"
#warning "deprecated path"   /* #warning is not ISO C (widely supported) */

#error causes a compile-time error with the message; #warning is a non-standard warning.

Common Mistakes

Undefined Behavior

that does. The preprocessor directives are well-defined.

Portability

macros where possible (__STDC_VERSION__).

Under the Hood

Conditional compilation is pure preprocessing; excluded code never reaches the compiler. This means you can use platform-specific syntax inside #if blocks without breaking other platforms.

Practical Usage

compilers.

implementation-defined.

Exercises

1. Write a header with an include guard and include it twice in one TU to verify no duplicate errors. 2. Write a program that prints which C standard it is compiled under using __STDC_VERSION__. 3. Use #if to select between two implementations of a function based on a macro. 4. Demonstrate #error by triggering it.

Deep Challenge

Write a portable header that selects an appropriate UNUSED-style annotation for GCC, Clang, MSVC, and "unknown compiler," using only conditional compilation and predefined macros. Explain each branch and the portability limits.

References

§6.10.5 (#error).

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.pp.conditional05
c.pp.pragma04