C Notes
Complete guide to the C preprocessor — what it is, how it works, preprocessing stages, all preprocessor directives overview, #pragma, #error, #line, predefined macros, practical examples.
Before your C code is compiled into machine language, it goes through a hidden transformation step called preprocessing. The preprocessor reads your source file, processes all lines starting with #, and produces a modified source file that the compiler actually compiles. Understanding this step explains many C behaviors that otherwise seem magical.
What is the C Preprocessor?
The preprocessor is a text processing tool that runs before compilation. It handles:
- File inclusion (
#include) - Macro expansion (
#define) - Conditional compilation (
#ifdef,#ifndef) - Other directives (
#pragma,#error,#line)
| #include | (expanded) | |||||
|---|---|---|---|---|---|---|
| #define | ────────→ | cpp | ────────→ | No # lines | ──────→ | cc |
| actual code | ready code |
Compilation Stages
| Source | ───→ | Preprocessor | ───→ | Compiler | ───→ | Assemb | ───→ | Linker |
|---|---|---|---|---|---|---|---|---|
| .c file | (text manip) | (→ .s) | (→ .o) | (→ a.out) |
You can see the preprocessor output with:
All Preprocessor Directives
| Directive | Purpose |
|---|---|
#include | Insert contents of another file |
#define | Define a macro (constant or function-like) |
#undef | Undefine a macro |
#ifdef | If macro is defined |
#ifndef | If macro is NOT defined |
#if | If expression is true |
#elif | Else if |
#else | Else |
#endif | End conditional block |
#pragma | Compiler-specific instruction |
#error | Generate compilation error message |
#warning | Generate compilation warning (GCC extension) |
#line | Change line number for error messages |
Seeing the Preprocessor in Action
After preprocessing (gcc -E input.c):
Predefined Macros
C provides several built-in macros (no #define needed):
#include <stdio.h>
int main() {
printf("File: %s\n", __FILE__);
printf("Line: %d\n", __LINE__);
printf("Date: %s\n", __DATE__);
printf("Time: %s\n", __TIME__);
printf("Function: %s\n", __func__);
#ifdef __STDC__
printf("Standard C compiler\n");
#endif
#ifdef __STDC_VERSION__
printf("C Standard version: %ld\n", __STDC_VERSION__);
#endif
return 0;
}File: predefined.c Line: 4 Date: Jun 13 2026 Time: 14:30:05 Function: main Standard C compiler C Standard version: 201710
| Macro | Value |
|---|---|
__FILE__ | Current filename (string) |
__LINE__ | Current line number (integer) |
__DATE__ | Compilation date "Mon DD YYYY" |
__TIME__ | Compilation time "HH:MM:SS" |
__func__ | Current function name (C99) |
__STDC__ | Defined as 1 if standard C compiler |
__STDC_VERSION__ | C standard version (199901L, 201112L, etc.) |
#pragma Directive
#pragma gives compiler-specific instructions:
// Suppress padding in struct (GCC/Clang)
#pragma pack(push, 1)
struct PackedData {
char c; // 1 byte
int i; // 4 bytes (no padding before!)
short s; // 2 bytes
};
#pragma pack(pop)
// Disable specific warning (MSVC)
#pragma warning(disable : 4996)
// Mark function as deprecated (GCC)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
void old_function() {
int unused = 0;
}
#pragma GCC diagnostic pop
// Ensure header is included once (most compilers)
#pragma once#error and #warning
Generate compilation messages:
#ifndef VERSION
#error "VERSION must be defined! Use -DVERSION=\"1.0\""
#endif
#if VERSION < 2
#warning "Deprecated API — upgrade to version 2.0"
#endif#line Directive
Changes the line number the compiler reports in error messages:
#line 100 "virtual_file.c"
// Compiler now thinks this is line 100 of "virtual_file.c"
int x = invalid; // Error will say: virtual_file.c:100This is used by code generators that produce C files from another source language.
Stringification and Token Pasting
#include <stdio.h>
// # converts macro argument to string literal
#define PRINT_VAR(var) printf(#var " = %d\n", var)
// ## concatenates tokens
#define MAKE_FUNC(name) void func_##name() { printf("Called " #name "\n"); }
MAKE_FUNC(hello)
MAKE_FUNC(world)
int main() {
int score = 95;
PRINT_VAR(score); // Expands to: printf("score" " = %d\n", score);
func_hello();
func_world();
return 0;
}score = 95 Called hello Called world
Preprocessor vs Compiler
| Feature | Preprocessor | Compiler |
|---|---|---|
| When it runs | Before compilation | After preprocessing |
| What it sees | Text (tokens) | Language syntax (AST) |
| Type awareness | None (just text) | Full type system |
| Scope rules | None (global text) | Block scoping |
| Error detection | Minimal | Full semantic checking |
| Output | Modified .c text | Assembly/object code |
Interview Questions
Q1: What is the C preprocessor and when does it run?
The preprocessor is a text-processing tool that runs before compilation. It handles file inclusion (#include), macro expansion (#define), and conditional compilation (#ifdef). It works on text tokens, not on C language semantics — it has no concept of types, scope, or syntax.
Q2: How can you see the preprocessor output?
Use gcc -E source.c which stops after preprocessing and outputs the expanded text. This is invaluable for debugging macro issues — you can see exactly what the compiler will receive.
Q3: What are predefined macros and give three examples?
Predefined macros are automatically defined by the compiler. Examples: __FILE__ (current filename), __LINE__ (current line number), __DATE__ (compilation date). They're useful for debugging, logging, and conditional compilation based on compiler features.
Q4: What's the difference between #pragma once and include guards?
Both prevent multiple inclusion of headers. #pragma once is simpler but non-standard (widely supported though). Include guards (#ifndef HEADER_H / #define HEADER_H / #endif) are standard C and guaranteed portable. Use include guards for maximum portability.
Summary
- The preprocessor runs before the compiler, transforming source text
- All directives start with
#and are processed line by line - Key directives:
#include,#define,#ifdef/#endif,#pragma - Predefined macros provide file, line, date, and version info
#stringifies tokens;##concatenates tokens- Use
gcc -Eto inspect preprocessor output when debugging macros - The preprocessor is unaware of C types, scope, or syntax
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for C Preprocessor – Overview and Directives.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this C Programming topic.
Search Terms
c-programming, c programming, programming, preprocessor, directives, c preprocessor – overview and directives
Related C Programming Topics