C Notes
Complete guide to conditional compilation in C — #ifdef, #ifndef, #if, #elif, #else, #endif directives, platform-specific code, debug builds, feature toggles, practical examples.
Conditional compilation lets you include or exclude code based on compile-time conditions. This is how C programs handle platform differences (Windows vs Linux), debug/release builds, feature flags, and optional dependencies — all from a single source file.
Why Conditional Compilation?
| Windows Build | Linux Build | |
|---|---|---|
| (Windows code) | (Linux code) |
The Directives
| Directive | Meaning |
|---|---|
#ifdef MACRO | If MACRO is defined (any value) |
#ifndef MACRO | If MACRO is NOT defined |
#if expr | If expression evaluates to non-zero |
#elif expr | Else if expression is non-zero |
#else | Otherwise |
#endif | End of conditional block |
#ifdef / #ifndef — Check if Defined
Program starting... [DEBUG] Debug mode is ON [DEBUG] Extra verbose output enabled [NOTE] This is not a release build Feature X is not available
Defining Macros from the Command Line
You can define macros without modifying source code:
#if — Expression-Based Conditions
#include <stdio.h>
#define VERSION 3
#define API_LEVEL 21
int main() {
#if VERSION >= 3
printf("Using new API (v3+)\n");
#elif VERSION >= 2
printf("Using legacy API (v2)\n");
#else
printf("Very old version — not supported\n");
#endif
#if API_LEVEL > 20 && VERSION >= 3
printf("Advanced features available\n");
#endif
#if 0
// This code is completely excluded from compilation
// Useful for "commenting out" large blocks
printf("This will never execute\n");
#endif
return 0;
}Using new API (v3+) Advanced features available
Platform-Specific Code
#include <stdio.h>
// Platform detection (automatically defined by compilers)
#ifdef _WIN32
#include <windows.h>
#define CLEAR_SCREEN() system("cls")
#define PATH_SEPARATOR '\\'
#elif defined(__linux__)
#include <unistd.h>
#define CLEAR_SCREEN() system("clear")
#define PATH_SEPARATOR '/'
#elif defined(__APPLE__)
#include <unistd.h>
#define CLEAR_SCREEN() system("clear")
#define PATH_SEPARATOR '/'
#else
#define CLEAR_SCREEN() ((void)0)
#define PATH_SEPARATOR '/'
#endif
void sleep_ms(int ms) {
#ifdef _WIN32
Sleep(ms);
#else
usleep(ms * 1000);
#endif
}
int main() {
#ifdef _WIN32
printf("Running on Windows\n");
#elif defined(__linux__)
printf("Running on Linux\n");
#elif defined(__APPLE__)
printf("Running on macOS\n");
#endif
printf("Path separator: '%c'\n", PATH_SEPARATOR);
return 0;
}Running on Linux Path separator: '/'
Common Platform Detection Macros
| Macro | Platform |
|---|---|
_WIN32 | Windows (32 and 64-bit) |
_WIN64 | Windows 64-bit only |
__linux__ | Linux |
__APPLE__ | macOS / iOS |
__unix__ | Any Unix-like OS |
__ANDROID__ | Android |
__GNUC__ | GCC compiler |
_MSC_VER | Microsoft Visual C++ |
__clang__ | Clang compiler |
Debug/Release Build Pattern
Compile with debug: gcc -DDEBUG program.c -o program_debug Compile for release: gcc -O2 program.c -o program_release
Feature Toggles
[LOG] Processing: user_record_001 [ENC] Encrypting data... Data processed. [LOG] Processing: transaction_002 [ENC] Encrypting data... Data processed.
The defined() Operator
Compiler Version Checks
#include <stdio.h>
int main() {
#ifdef __GNUC__
printf("GCC version: %d.%d.%d\n",
__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#endif
#ifdef __clang__
printf("Clang version: %d.%d\n",
__clang_major__, __clang_minor__);
#endif
// Use C11 features only if available
#if __STDC_VERSION__ >= 201112L
printf("C11 features available\n");
#elif __STDC_VERSION__ >= 199901L
printf("C99 features available\n");
#else
printf("Pre-C99 compiler\n");
#endif
return 0;
}GCC version: 11.4.0 C11 features available
Interview Questions
Q1: What is the difference between #ifdef and #if?
#ifdef MACRO only checks if a macro is defined (any value, even empty). #if expr evaluates an integer expression that can involve comparisons, logical operators, and defined(). Use #ifdef for simple existence checks, #if for value-based decisions.
Q2: How do you write code that compiles on both Windows and Linux?
Use platform-detection macros: #ifdef _WIN32 for Windows, #ifdef __linux__ for Linux. Wrap platform-specific code (headers, system calls, path handling) in these conditionals. Keep shared logic outside the blocks.
Q3: What's the difference between #if 0 and comments for disabling code?
#if 0 ... #endif cleanly excludes code from compilation and can nest (unlike /* */ comments which can't nest). It's preferred for temporarily disabling large code blocks during debugging. The code inside is still syntax-highlighted by most editors.
Q4: What does -DDEBUG do on the command line?
It defines the macro DEBUG as if #define DEBUG 1 appeared at the top of every source file. This enables any code inside #ifdef DEBUG blocks. It lets you control build behavior without modifying source code — useful for CI/CD pipelines.
Q5: Can #if evaluate function calls?
No. The preprocessor runs before compilation — functions don't exist yet. #if can only evaluate integer constant expressions: literals, macros that expand to integers, sizeof (in some compilers), and the defined() operator.
Summary
- Conditional compilation includes/excludes code at compile time
#ifdef/#ifndefcheck if a macro is defined#if/#elif/#elseevaluate constant expressions- Use for: platform differences, debug/release, feature flags
- Command-line
-DMACROdefines macros without editing source defined()operator allows complex boolean logic in#if#if 0...#endifis the cleanest way to disable code blocks
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Conditional Compilation in C – #ifdef, #ifndef, #if.
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, conditional, compilation, conditional compilation in c – #ifdef, #ifndef, #if
Related C Programming Topics