C Preprocessors
C - Preprocessors
Section titled “C - Preprocessors”The C Preprocessor (CPP) is a program that processes your source code before it is passed to the actual compiler. It acts as a text manipulation tool, interpreting special instructions called preprocessor directives.
Preprocessor directives start with a hash symbol (#) and are typically placed at the beginning of a line. They are not C statements and don’t end with semicolons.
Common uses of the preprocessor include:
- File Inclusion: Including header files (
#include). - Macro Definition: Defining symbolic constants and function-like macros (
#define). - Conditional Compilation: Including or excluding parts of the code based on conditions (
#if,#ifdef,#ifndef,#else,#elif,#endif). - Other Directives: Removing macro definitions (
#undef), emitting errors (#error), providing compiler-specific instructions (#pragma).
Key Preprocessor Directives
Section titled “Key Preprocessor Directives”| Directive | Description |
|---|---|
#include | Inserts the content of a specified header file. <filename.h> searches standard system directories; "myheader.h" searches the current directory first, then standard directories. |
#define | Defines a preprocessor macro. Can define symbolic constants (#define PI 3.14) or function-like macros (#define MAX(a,b) ((a) > (b) ? (a) : (b))). Text substitution occurs before compilation. |
#undef | Removes a previously defined macro definition. |
#ifdef MACRO_NAME | Conditional compilation: Includes subsequent code if MACRO_NAME is defined (#defined). Ends with #endif. |
#ifndef MACRO_NAME | Conditional compilation: Includes subsequent code if MACRO_NAME is not defined. Often used for include guards in header files. Ends with #endif. |
#if constant_expression | Conditional compilation: Includes subsequent code if the constant_expression evaluates to non-zero (true). Ends with #endif. |
#else | Used within #if, #ifdef, #ifndef blocks to provide alternative code if the condition is false. |
#elif constant_expression | Else if. Used within #if blocks to test another condition if the previous #if/#elif was false. |
#endif | Marks the end of a conditional compilation block (#if, #ifdef, #ifndef). |
#error message | Causes the preprocessor to stop and issue an error message containing the specified text. Useful for sanity checks during compilation. |
#pragma directive | Provides implementation-defined instructions to the compiler (e.g., controlling optimizations, warnings, structure packing). Highly compiler-specific. |
File Inclusion (#include)
Section titled “File Inclusion (#include)”Essential for using standard library functions and declarations from custom header files.
#include <stdio.h> // Include standard input/output library#include <stdlib.h> // Include standard library (malloc, exit, etc.)#include "my_utils.h" // Include a custom header file from the projectMacro Definition (#define)
Section titled “Macro Definition (#define)”- Symbolic Constants: Replaces occurrences of the macro name with the defined text.
#define BUFFER_SIZE 1024#define COMPANY_NAME "My Tech Corp."
char buffer[BUFFER_SIZE];printf("Welcome to %s\n", COMPANY_NAME);- Function-Like Macros: Can accept arguments. Warning: Be careful with parentheses to avoid operator precedence issues, and be aware that arguments might be evaluated multiple times.
#define SQUARE(x) ((x) * (x)) // Parentheses are crucial!#define IS_ODD(n) ((n) % 2 != 0)
int main(void) { int a = 5; printf("Square of %d is %d\n", a, SQUARE(a)); // Output: 25 printf("Square of %d is %d\n", a + 1, SQUARE(a + 1)); // Expands to ((a + 1) * (a + 1)) -> 36
if (IS_ODD(a)) { printf("%d is odd\n", a); // Output: 5 is odd } return 0;}Modern C often prefers const variables for constants and inline functions (or regular functions) over complex function-like macros due to better type safety and debugging.
Conditional Compilation (#if, #ifdef, etc.)
Section titled “Conditional Compilation (#if, #ifdef, etc.)”Allows compiling different code sections based on conditions evaluated at compile time. Useful for platform-specific code, debugging builds, or feature flags.
#include <stdio.h>
#define DEBUG_MODE 1 // Set to 1 for debug builds, 0 for release#define TARGET_OS "Linux"
int main(void) {
#if DEBUG_MODE == 1 printf("Debug mode is ON\n");#else printf("Release mode\n");#endif
#ifdef TARGET_OS printf("Target OS defined as: %s\n", TARGET_OS);#else printf("Target OS not defined.\n");#endif
#if defined(TARGET_OS) && (TARGET_OS[0] == 'L') // Check if defined and starts with 'L' printf("Looks like a Linux target.\n");#elif defined(TARGET_OS) && (TARGET_OS[0] == 'W') printf("Looks like a Windows target.\n");#endif
#ifndef LICENSE_KEY // Check if LICENSE_KEY is NOT defined #error "License key must be defined!" // Stop compilation#endif
return 0;}You can define macros via #define or externally using compiler flags (e.g., gcc -DDEBUG_MODE=1 mycode.c).
Predefined Macros
Section titled “Predefined Macros”C standards define several useful macros that provide information about the compilation environment. These should not be modified.
| Macro | Description |
|---|---|
__DATE__ | The compilation date as a string literal (e.g., “Jan 01 2023”). |
__TIME__ | The compilation time as a string literal (e.g., “10:30:00”). |
__FILE__ | The current source filename as a string literal. |
__LINE__ | The current line number in the source file as an integer constant. |
__STDC__ | Defined as 1 if the compiler conforms to the C standard (specific version may depend on compiler flags). |
__STDC_VERSION__ | (C95+) Defined as a long integer representing the standard version (e.g., 201112L for C11, 201710L for C17). |
Example:
printf("Compiled on %s at %s\n", __DATE__, __TIME__);printf("Function '%s' is at %s:%d\n", __func__, __FILE__, __LINE__); // __func__ (C99) provides function nameThe preprocessor is a powerful tool for code organization and conditional compilation in C.