Preprocessor Directives in C

In this tutorial, you will learn about preprocessor directives in C. These are special commands that help the program before it compiles. You will see important directives like #include, #define, #ifdef, #ifndef, and #pragma. They help add files, create shortcuts, and control parts of the code. By the end, you will know how to use these commands to write simple and clear C programs.

Contents:

  1. Introduction to Preprocessor Directives
  2. Working of the C Preprocessor
  3. Types of Preprocessor Directives
  4. Macro Definitions: #define in C
  5. File Inclusion: #include in C
  6. Conditional Compilation: #if, #else, #elif, #endif in C
  7. Undefining Macros: #undef in C
  8. #error Directive in C
  9. #pragma Directive in C
  10. #line Directive in C
  11. Advantages of Using Preprocessor Directives in C
  12. FAQs on Preprocessor Directives in C

Introduction to Preprocessor Directives

Preprocessor directives in C are commands that are processed before the compilation of the program. These directives start with # and are used to include files, define constants, and manage code conditions.

Preprocessor directives help the program before it compiles. They do tasks like:

  • Adding header files using #include
  • Creating constants or shortcuts with #define
  • Running code only in certain cases using #if, #ifdef, #ifndef, and #endif
  • Removing a defined name using #undef
  • Adding special code for testing or for different systems

These commands do not end with semicolons. They are not normal C statements. Instead, they give instructions to the preprocessor.

Example:

advertisement
#include <stdio.h>   // File Inclusion
#define PI 3.14      // Macro Definition
 
int main() {
    printf("Value of PI: %f\n", PI); // Macro Expansion
    return 0;
}

Output:

Value of PI: 3.140000

Free 30-Day Python Certification Bootcamp is Live. Join Now!

Working of the C Preprocessor

The C preprocessor is a tool that processes your source code before compilation. It performs the following key operations:

  1. File Inclusion: It replaces #include directives with the contents of the specified header file.
  2. #include <stdio.h>  // Replaced with the full content of stdio.h
  3. Macro Expansion: It replaces all macro identifiers with their corresponding values or code blocks.
  4. #define PI 3.14
    float area = PI * r * r;  // PI is replaced by 3.14
  5. Conditional Compilation: It compiles parts of the code based on conditions.
  6. #ifdef DEBUG
    printf("Debugging\n");
    #endif
  7. Removing Comments: The preprocessor removes all single-line (//) and multi-line (/* */) comments from the code.
  8. Macro Functions: Macros can also work like functions with parameters.
  9. #define SQUARE(x) ((x)*(x))
    int y = SQUARE(5);  // Expands to ((5)*(5))

Types of Preprocessor Directives

C provides several types of preprocessor directives that control how the source code is processed before compilation. These include:

Type Directive(s) Purpose
Macro Definition #define, #undef Define and undefine macros
File Inclusion #include Include standard or user-defined files
Conditional Compilation #if, #ifdef, #ifndef, etc. Compile code based on conditions
Error Handling #error Generate compiler error with message
Line Control #line Modify line numbers in error messages
Null Directive # Does nothing; placeholder sometimes used

Macro Definitions: #define in C

In C, the #define preprocessor directive is used to define macros, which are constant values or code fragments that are replaced before compilation.

Syntax of #define

advertisement
#define MACRO_NAME value_or_expression
  • MACRO_NAME → The identifier (constant or function-like macro).
  • value_or_expression → The replacement text.

Types of Macros in C

1. Object-like Macros (Constant Macros)

These macros define constant values.

Example:

#include <stdio.h>
#define PI 3.14159  // Defining a constant macro
 
int main() {
    printf("Value of PI: %f\n", PI);
    return 0;
}

Output:

Value of PI: 3.141590

2. Function-like Macros (Parameterized Macros)

These macros work like inline functions.

Example:

#include <stdio.h>
#define AREA(l, w) (l * w)  // Macro for calculating area
 
int main() {
    printf("Area of rectangle: %d\n", AREA(5, 10));
    return 0;
}

Output:

Area of rectangle: 50

3. Conditional Macros (#ifdef, #ifndef, #endif)

Used for conditional compilation.

Example:

#include <stdio.h>
#define DEBUG  // Enable Debugging Mode
 
int main() {
    #ifdef DEBUG
        printf("Debug Mode: Active\n");
    #else
        printf("Debug Mode: Inactive\n");
    #endif
    return 0;
}

Output:

Debug Mode: Active

4. Multi-line Macros (\ for Continuation)

Macros can span multiple lines using \.

Example:

#include <stdio.h>
#define PRINT_DETAILS(name, age) \
    printf("Name: %s\n", name);  \
    printf("Age: %d\n", age)
 
int main() {
    PRINT_DETAILS("Sanfoundry", 10);
    return 0;
}

Output:

Name: Sanfoundry
Age: 10

File Inclusion: #include in C

The #include directive is a preprocessor command used to include external files in a C program. These files typically contain function declarations, macros, and definitions that help in modular programming.

Types of #include Directives

1. Angle Bracket (<>) Syntax: Used for standard library files.

#include <stdio.h>  // Includes standard input-output library

2. Double Quotes (“”) Syntax: Used for user-defined header files.

#include "myheader.h"  // Includes a custom header file

Example 1: Including a Standard Library Header

#include <stdio.h>
 
int main()
{
    printf("Sanfoundry C MCQs!\n");  // Uses printf() from stdio.h
    return 0;
}

Output:

Sanfoundry C MCQs!

Example 2: Including a User-Defined Header File

Step 1: Create a file named myheader.h

// myheader.h
#define PI 3.14159
void greet() {
    printf("Welcome to C Programming!\n");
}

Step 2: Include myheader.h in the main program

#include <stdio.h>
#include "myheader.h"  // Including user-defined header file
 
int main() {
    greet();
    printf("Value of PI: %f\n", PI);
    return 0;
}

Output:

Welcome to C Programming!
Value of PI: 3.141590

Conditional Compilation: #if, #else, #elif, #endif in C

Conditional compilation allows the compiler to selectively compile portions of the program based on conditions. This is useful for:

  • Debugging
  • Platform-specific code
  • Including/excluding features

Preprocessor Directives for Conditional Compilation

Directive Description
#if Checks if a condition is true
#else Executes if the #if condition is false
#elif “Else if” statement for multiple conditions
#endif Marks the end of conditional compilation

Example 1: Using #if and #else

#include <stdio.h>
 
#define DEBUG 1  // Set to 0 to disable debug mode
 
int main() {
    #if DEBUG
        printf("Debugging is enabled\n");
    #else
        printf("Debugging is disabled\n");
    #endif
    return 0;
}

Output (if DEBUG is 1):

Debugging is enabled

Output (if DEBUG is 0):

Debugging is disabled

Example 2: Using #elif for Multiple Conditions

#include <stdio.h>
 
#define OS 2  // 1 for Windows, 2 for Linux, 3 for Mac
 
int main() {
    #if OS == 1
        printf("Running on Windows\n");
    #elif OS == 2
        printf("Running on Linux\n");
    #elif OS == 3
        printf("Running on Mac\n");
    #else
        printf("Unknown OS\n");
    #endif
    return 0;
}

Output (if OS is 2):

Running on Linux

Example 3: Using #ifdef and #ifndef

#include <stdio.h>
 
#define FEATURE_ENABLED
 
int main() {
    #ifdef FEATURE_ENABLED
        printf("Feature is enabled\n");
    #else
        printf("Feature is disabled\n");
    #endif
    return 0;
}

Output (since FEATURE_ENABLED is defined):

Feature is enabled

Undefining Macros: #undef in C

The #undef preprocessor directive removes a previously defined macro. It is useful when:

  • You want to redefine a macro with a new value.
  • You need to ensure a macro is undefined before defining it again.

Syntax:

#undef MACRO_NAME

MACRO_NAME is the macro that was previously defined using #define.

Example 1: Using #undef to Redefine a Macro

#include <stdio.h>
 
#define MAX_SIZE 100
 
int main() {
    printf("Initial MAX_SIZE: %d\n", MAX_SIZE);
 
    #undef MAX_SIZE  // Undefining the macro
    #define MAX_SIZE 50  // Redefining the macro
 
    printf("Updated MAX_SIZE: %d\n", MAX_SIZE);
 
    return 0;
}

Output:

Initial MAX_SIZE: 100
Updated MAX_SIZE: 50

This program shows how to define, remove, and change a macro in C. First, MAX_SIZE is set to 100 using #define. The program prints this value. Then, #undef removes the old MAX_SIZE, and it is redefined as 50. The program prints the new value. This shows how macros can be updated before the code is compiled.

Example 2: Ensuring a Macro is Undefined Before Defining It

#include <stdio.h>
 
#define MODE 1
 
#undef MODE  // Ensure it is undefined before redefining
#define MODE 2
 
int main() {
    printf("Current MODE: %d\n", MODE);
    return 0;
}

Output:

Current MODE: 2

This program shows how to change a macro value using #undef and #define. First, MODE is set to 1. Then, #undef removes the old value. After that, MODE is redefined as 2. When the program runs, it prints the new value. This shows how you can update macros before the code is compiled.

#error Directive in C

The #error directive forces compilation to stop and displays a custom error message. It is useful for:

  • reventing compilation if a condition is not met.
  • Ensuring compatibility with specific compilers or platforms.
  • Debugging configuration issues.

Syntax

#error "Custom error message"

Example: Using #error to Check Compiler Version

#include <stdio.h>
 
#if __STDC_VERSION__ < 199901L
    #error "C99 or later is required for this program."
#endif
 
int main() {
    printf("Program compiled successfully!\n");
    return 0;
}

Output (if using an older compiler):

C99 or later is required for this program.

This program uses a preprocessor check to make sure the C compiler supports C99 or later. The #if directive checks the version of the C standard. If it’s older than C99, the #error directive stops the compilation and shows a message. If the version is fine, the program compiles and prints a success message. This helps ensure the code runs only on compatible compilers.

#pragma Directive in C

The #pragma directive provides compiler-specific instructions, allowing:

  • Compiler optimizations.
  • Warnings control.
  • Code segmenting.

Common Uses of #pragma in C

1. Suppress Warnings (#pragma warning)

#pragma warning(disable: 4996)  // Disables specific warning in MSVC
#include <stdio.h>
 
int main() {
    char name[20];
    gets(name);  // No warning due to #pragma
    printf("Hello, %s\n", name);
    return 0;
}

2. Optimize Code (#pragma optimize)

#pragma optimize("g", off)  // Disable global optimizations
#include <stdio.h>
 
int main() {
    printf("Optimizations are disabled!\n");
    return 0;
}

3. Region Marking (#pragma region and #pragma endregion)

#pragma region Debugging
void debugFunction() {
    printf("Debug Mode\n");
}
#pragma endregion

4. Pack Structure (#pragma pack)

#include <stdio.h>
 
#pragma pack(1)  // Align structure to 1-byte boundary
struct Example {
    char a;
    int b;
};
 
int main() {
    printf("Size of struct: %lu\n", sizeof(struct Example));
    return 0;
}

#line Directive in C

The #line directive in C is used to change the current line number and filename reported by the compiler. It is mainly used for:

  • Debugging and error tracking.
  • Simulating output from code generators.
  • Modifying error messages to reflect different source files.

Syntax

#line line_number "optional_filename"
  • line_number → Sets the line number to start counting from.
  • “optional_filename” → Changes the filename (optional).

Example 1: Changing Line Number for Error Messages

#include <stdio.h>
 
int main() {
    printf("Line before #line: %d\n", __LINE__); // Prints current line number
 
    #line 100  // Change the line number to 100
    printf("Line after #line: %d\n", __LINE__);
 
    return 0;
}

Output:

Line before #line: 5
Line after #line: 100
  • __LINE__ is a predefined macro that stores the current line number.
  • #line 100 sets the next line to 100, affecting __LINE__.

Example 2: Changing Both Line Number and Filename

#include <stdio.h>
 
int main() {
    printf("Current File: %s, Line: %d\n", __FILE__, __LINE__);
 
    #line 200 "newfile.c"  // Change both filename and line number
    printf("Modified File: %s, Line: %d\n", __FILE__, __LINE__);
 
    return 0;
}

Output:

Current File: original.c, Line: 5
Modified File: newfile.c, Line: 200
  • __FILE__ is a predefined macro for the current filename.
  • After #line 200 “newfile.c”, the compiler treats the following lines as if they belong to newfile.c starting at line 200.

Advantages of Using Preprocessor Directives in C

  • By using #define to name constants or macros, the code becomes easier to read and understand. It also makes maintenance simpler, since changes need to be made only at one place.
  • Directives like #if, #ifdef, and #ifndef allow selective compilation of code blocks. This is especially helpful for enabling debug code or building for different environments.
  • With #include, you can split your code into multiple files such as headers and source files. This modular approach promotes code reuse, readability, and cleaner structure.
  • Preprocessor checks can detect the target OS or compiler and include platform-specific code. This helps in writing cross-platform code that works on Windows, Linux, and macOS.
  • You can use macros to enable or disable debugging output or test features quickly. This helps in testing specific parts of the code without changing the core logic.
  • Macros can replace repeated expressions or logic, reducing redundant code. This not only saves space but also ensures consistency throughout the program.
  • Since preprocessing happens before actual compilation, unnecessary code can be excluded early. This can reduce compilation time and make the build process more efficient.

FAQs on Preprocessor Directives in C

1. What is a preprocessor directive in C?
Preprocessor directives are instructions that are processed by the compiler before actual compilation starts. They begin with a # symbol (e.g., #include, #define, #if).

2. What is the use of #define in C?
#define is used to create symbolic constants or macros. It helps improve code readability and makes updating values easier.

3. What is the purpose of #include?
#include is used to include the contents of another file (usually a header file) into the current file before compilation.

4. What is conditional compilation?
Conditional compilation lets you compile specific parts of code based on defined conditions using directives like #if, #ifdef, #else, and #endif.

5. How do I prevent multiple inclusions of a header file?
By using conditional preprocessor checks, you can ensure a file is included only once, avoiding redefinition errors.

6. What does #undef do?
It cancels a previously defined macro so it can be redefined or removed from the current compilation scope.

7. Can we define functions using macros?
Yes, macros can mimic functions by substituting repetitive code, though they do not support type checking like regular functions.

8. Are preprocessor directives part of the C language syntax?
No, they are separate instructions handled by the preprocessor before actual C code compilation begins.

Key Points to Remember

Here is the list of key points we need to remember about “Preprocessor Directives in C”.

  • Preprocessor directives in C are commands that run before compilation and start with #, used for file inclusion, macros, and conditional compilation.
  • #define is used to create constant macros and function-like macros, while #undef removes a macro definition.
  • #include adds external files, using <> for standard headers and “” for user-defined files.
  • Conditional compilation directives like #if, #ifdef, #ifndef, and #endif help compile specific code sections based on conditions.
  • #error stops compilation with a custom error message, useful for version checks and debugging.
  • #pragma provides compiler-specific instructions for optimizations, warnings, and structure packing.
  • #line changes the reported line number and filename, aiding debugging and error tracking.
  • Preprocessor directives improve code modularity, readability, debugging, platform compatibility, and efficiency.

advertisement
advertisement
Subscribe to our Newsletters (Subject-wise). Participate in the Sanfoundry Certification contest to get free Certificate of Merit. Join our social networks below and stay updated with latest contests, videos, internships and jobs!

Youtube | Telegram | LinkedIn | Instagram | Facebook | Twitter | Pinterest
Manish Bhojasia - Founder & CTO at Sanfoundry
Manish Bhojasia, a technology veteran with 20+ years @ Cisco & Wipro, is Founder and CTO at Sanfoundry. He lives in Bangalore, and focuses on development of Linux Kernel, SAN Technologies, Advanced C, Data Structures & Alogrithms. Stay connected with him at LinkedIn.

Subscribe to his free Masterclasses at Youtube & discussions at Telegram SanfoundryClasses.