Preprocessor in C Programming

This article is the continuation of the Series on the C programming tutorial and carries the discussion on C language programming and its implementation. It aims to provide easy and practical examples for understanding the C program. In our last article, we have seen the Functions in C programming. In this article, we are going to explain Preprocessor in C Programming.

Preprocessor in C Programming

What is a preprocessor in C?

In C programming, a preprocessor is a tool that performs text preprocessing before the compilation of the source code. It is a separate step in the compilation process that is responsible for manipulating the source code before it is passed to the compiler. There are multiple process is happening during the compilation. We have discussed those things in this article already. Please check that.

The preprocessor directives in C start with a hash symbol (#) and are processed by the preprocessor. These directives are used to include header files, define macros, and perform conditional compilation.

The preprocessor plays a crucial role in C programming by allowing developers to create modular and reusable code, customize the behavior of the compiler, and enable conditional compilation based on different requirements.

What is the use of a preprocessor in C programming?

A preprocessor in C programming is a tool that processes source code before it is compiled. It performs tasks such as including header files, defining constants, and performing macro expansion. It helps in making code more modular and efficient.

To instruct the preprocessor you have to use the preprocessor Directive which lets you compile the code depending on some conditional command.

What is the List of preprocessor Directives available?

There are many preprocessor Directives available. Some of them are,

  • #include: It inserts a header file from another file.
  • #define: It is a preprocessor used as a macro.
  • #undef: It undefines a preprocessor macro.
  • #if: Tests if a compile-time condition is true.
  • #ifdef: Return true if the macro is defined.
  • #ifndef: Return true if the macro is not defined.
  • #else: The alternative for #if.
  • #elif: else and #if in one statement.
  • #endif: Ends preprocessor conditional.
  • #warning: It can be used for warnings.
  • #error: It is used to stop compilation.

How many types of preprocessor Directives?

There are four types of preprocessors given below.

File Inclusion

File inclusion preprocessor command (#include) tells the compiler to include a file in the source code program. Generally, two types of files are included in the program.

Why do use file inclusion?

  • When we want to maintain the modularized code, we need to create multiple source files. In that case, we may need to include the other files in any of the source code.
  • There are many libraries available. For example string. If we want to use the string functions, we can include the string.h file (#include string.h) and we can use the functions available in that file.

Types of header files

  • Standard/Pre-define header file (#include <file_name.h>)
  • User-defined header file (#include "file_name.h")
Standard/Pre-define header file

It contains definitions of pre-defined functions that have been developed and are already available like stdio.h, string.h etc. If you #include with <angular brackets> it means that your operating system will be searching for the particular pre-define location like SDK or PATH.

Syntax

// Compiler look for the file in specified list of directories only
#include <filename.h> 

Example

#include <stdio.h>
#include <string.h>

int main()
{
    char str[50];

    printf("Enter String:- ");
    gets(str);
    puts(str);

    return 0;
}

In this above program, I have used two files of the standard header file (stdio.h, string.h) which is readily available already. So we don’t have to create it on our own.

Output

Enter String:- EmbeTronicX
EmbeTronicX
User-defined header file

It contains definitions of user-defined functions that have been created by us and included in the program like “my_header.h” etc. If you #include with "double quotes" it means that your operating system will be searching for both the user project location which is inside the source file as well as a particular pre-define location which is inside the standard library files.

// Compiler look for the file in current directory as well as specified list of directories
#include "file_name.h" 

Example

main.c file

#include <stdio.h>
#include "my_fun.h"
int main()
{
    int a,b,c;

    printf("Enter two numbers:- ");
    scanf("%d%d",&a,&b);
    c=add(a,b);
    printf("%d + %d = %d",a,b,c);
    
    return 0;
}

my_fun.h file

int add(int a, int b)
{
    return (a+b);
}

Output

Enter two numbers:- 5 2
5 + 2 = 7

Macros in C Programming

Macros

In C programming, a macro is a way to define a piece of code that can be reused throughout the program. It is a preprocessor directive that allows you to define constants, functions, or code snippets that can be easily substituted wherever they are called. Macros are defined using the #define directive and can help in simplifying complex code, improving readability, and reducing code duplication. When the program is compiled, the macro statements are replaced by their corresponding value or code. Overall, macros are a powerful tool for writing flexible and efficient code in C programming.

Note: While Declaring the MACRO the characters should always be used in uppercase and you should not use semicolon at the end while defining the macro statement in a program, it will be a good programming practice.

Example

#include <stdio.h>

#define AREA(r)   (3.1415 * r * r)

int main()
{
    float r,a;
    
    printf("Enter a radius:- ");
    scanf("%f",&r);
    a=AREA(r);
    printf("Area = %.1f",a);
    
    return 0;
}

In this above program, I have defined a macro called AREA. I’m reusing it in the code.

Output

Enter a radius:- 5
Area = 78.5

What is macro with an argument?

Whenever you convert a function with macro then that is called macro with argument, It is good to be used for a small program to define macro rather than use it for a big program. There is another example 2 in which you can declare the whole C statement in macro, given below.

Syntax

#define CUBE(n) n*n // (Cube---> Macro, (n)---> Macro Declartion, n*n---> Macro Defination)

Example 1

#include <stdio.h>

#define CUBE(n) (n*n)

int main()
{
    int i,n;
    
    printf("Enter a number:- ");
    scanf("%d",&n);
    i=CUBE(n);
    printf("Cube = %d",i);
    
    return 0;
}

Output

Enter a number:- 5
Cube = 25

Example 2

#include <stdio.h>

#define DECISION    (a>50 && a<100)

#define DISPLAY_1   printf("Greater then 50")

#define DISPLAY_2   printf("Less then 50")

int main()
{
    int a;
    printf("Enter a Number:- ");
    scanf("%d",&a);
    if(DECISION)
    {
      DISPLAY_1;
    }
    else
    {
      DISPLAY_2;
    }
}

In the above program, we have declared three macros (DECISION, DISPLAY_1, DISPLAY_2). Display macros used to print different prints. DECISION macro checks for the condition.

Output

Enter a Number:- 70
Greater then 50
Enter a Number:- 20
Less then 50

Multiline Macro in C

In C, macros are often used to define reusable code snippets. While most macros consist of a single line of code, it is also possible to create multiline macros. This can be achieved using the \ character to indicate that the macro continues onto the next line. Here’s an example:

#include <stdio.h>

#define PRINT_VALUES(a, b) \
    printf("Value of a: %d\n", a); \
    printf("Value of b: %d\n", b);

int main() {
    int x = 5;
    int y = 10;

    PRINT_VALUES(x, y);

    return 0;
}

In this example, we have defined a multiline macro called PRINT_VALUES that takes two arguments a and b. The macro is used to print the values of these arguments using printf statements. The \ character at the end of each line indicates that the macro continues onto the next line.

When the code is compiled and executed, the output will be:

Value of a: 5
Value of b: 10

Note that the use of multiline macros can be helpful in reducing code duplication and improving code readability. However, it’s important to use them judiciously and consider their impact on the code’s maintainability.

Macro vs Function

In C programming, macros, and functions are both important tools that serve different purposes. Here’s a brief comparison between macros and functions:

MacroFunction
Macros in C are defined using the #define preprocessor directive.Functions in C are defined using the function_name() format and can take parameters and return values.
Macros are used to define constant values or to define code snippets that need to be expanded inline.Functions are a fundamental part of C programming and are used to modularize code and improve code readability.
Macros are not type-safe and do not perform any kind of argument type checking.Functions provide type safety as the compiler can perform argument type checking.
Macros are expanded by the preprocessor before compilation, replacing the macro code with the corresponding definition.Functions are called at runtime and have a separate memory location for each invocation.
Macros have the advantage of being able to perform complex operations at compile-time, such as string concatenation or conditional compilation.Functions are generally more efficient than macros in terms of code size, especially for large and complex code blocks.
Macros can result in larger code size as they are copied wherever they are used.Functions are useful for implementing reusable code and promoting code reusability.
Example: #define ADD(a,b) a+bExample: int add(int a, int b)

Overall, macros and functions each have their own advantages and use cases. Choosing between them largely depends on the specific requirements and goals of your program.

Macro vs inline functions in C

In our next article, we will see the difference between macro and inline functions. in C programming.

Conditional Compilation in C Programming

Conditional compilation in C programming is a powerful feature that allows developers to selectively compile specific sections of code based on certain conditions. This feature is particularly useful when dealing with code that needs to work on different platforms, with variations in operating systems, hardware configurations, or compiler-specific directives.

What are the types of preprocessors used in conditional compilation?

These are the preprocessor directives, commonly used for conditional compilation in C programming.

#if

In C programming, the #if directive is part of the preprocessor directives used for conditional compilation. It tests whether a compile-time condition is true and determines whether the code within the corresponding block should be included during the compilation process.

The #if directive is often used in conjunction with other conditional directives such as #ifdef, #ifndef, #else, and #elif to create multiple branches of code execution based on different conditions.

Here is an example to illustrate the usage of #if:

#include <stdio.h>

#define NUMBER 10

int main()
{
#if NUMBER < 5
    printf("Number is less than 5.\n");
#endif

    return 0;
}

In this example, the NUMBER macro is defined as 10. The #if directive is used to test the condition NUMBER < 5 and determine which branch of code should be included during compilation. Since the condition is false, the code between the NUMBER < 5 and #endif is not included during the compilation.

The #if directive allows you to create logic branches in your code based on compile-time conditions, making your program more flexible and adaptable to different scenarios.

#endif

This directive marks the end of a conditional block. This should be used only with any of the below preprocessor directives.

#ifdef

This directive checks whether a macro or symbol is defined. If the specified macro or symbol is defined, the code between #ifdef and #endif will be included during the compilation process. Otherwise, it will be skipped.

#include <stdio.h>

#define DEBUG

int main() 
{
#ifdef DEBUG
        printf("Debug mode enabled.");
#endif
    return 0;
}

In the above program, printf("Debug mode enabled.") will be compiled only if the DEBUG macro is defined. So, the DEBUG macro is defined in the above program. So, that printf will be compiled.

#ifndef

This directive is the opposite of #ifdef. It checks whether a macro or symbol is not defined. If the specified macro or symbol is not defined, the code between #ifndef and #endif will be included during the compilation.

#include <stdio.h>

#define DEBUG

int main() 
{
#ifndef DEBUG
        printf("Debug mode is not enabled.");
#endif
    return 0;
}

In the above program, printf("Debug mode is not enabled.") will be compiled only if the DEBUG macro is not defined. So, the DEBUG macro is defined in the above program. So, that printf will not be compiled.

#else

This directive is used in conjunction with #ifdef or #ifndef. If the condition specified by #ifdef or #ifndef evaluates to false, the code between #else and #endif will be included instead.

#include <stdio.h>

#define DEBUG

int main() 
{
#ifdef DEBUG
        printf("Debug mode enabled.");
#else
        printf("Debug mode disabled.");
#endif
    
    return 0;
}

In this example, when the DEBUG macro is defined, the code between #ifdef DEBUG and #else will be included during compilation, and the output will be “Debug mode enabled.” Conversely, if the DEBUG macro is not defined, the code between #else and #endif will be included, and the output will be “Debug mode disabled.

#elif

This #elif directive is used in conjunction with conditional compilation to create multiple branches of code execution based on different conditions. It stands for “else if” and is often used in combination with #ifdef, #ifndef, and #else.

The #elif directive allows you to specify an additional condition to be checked if the previous condition specified by #if or #elif is false. If the additional condition evaluates to true, the code block following #elif will be included during the compilation. If the additional condition is false, the compiler will move on to the next #elif or #else directive.

Here’s an example to illustrate the usage of #elif:

#include <stdio.h>

#define NUMBER 10

int main() 
{
#if NUMBER < 5
        printf("Number is less than 5.\n");
#elif NUMBER > 5 && NUMBER < 10
        printf("Number is greater than 5 and less than 10.\n");
#else
        printf("Number is either less than or equal to 5, or greater than or equal to 10.\n");
#endif
    
    return 0;
}

In this example, the value of the NUMBER macro is 10. The first condition NUMBER < 5 is false, so the compiler moves on to the next #elif directive. The second condition NUMBER > 5 && NUMBER < 10 is true, so the code block following #elif is executed, and the output will be “Number is greater than 5 and less than 10.

The #elif directive allows you to create multiple conditions and branches of code, making your program more flexible and adaptable to different scenarios.

#if defined

In C programming, the #if defined directive is used for conditional compilation based on the existence of a specific macro or symbol in the code. The #if defined directive is often used in conjunction with other conditional directives such as #ifdef, #ifndef, #else, and #elif to selectively compile different sections of code based on the presence or absence of a macro or symbol.

Here’s the syntax of the #if defined directive:

#if defined(MACRO_NAME)
    // code to be compiled if the macro is defined
#else
    // code to be compiled if the macro is not defined
#endif

The defined keyword is used to test whether a macro or symbol is defined. If the specified macro or symbol is defined, the code between #if defined and #else will be included during the compilation process. Otherwise, the code between #else and #endif will be included.

Here’s an example to illustrate the usage of #if defined:

#include <stdio.h>

#define DEBUG

int main() {
#if defined(DEBUG)
        printf("Debug mode enabled.\n");
#else
        printf("Debug mode disabled.\n");
#endif

    return 0;
}

In this example, the DEBUG macro is defined using the #define directive. The #if defined(DEBUG) directive checks whether the DEBUG macro is defined. Since it is defined, the code between #if defined(DEBUG) and #else will be compiled, and the output will be “Debug mode enabled.

If the DEBUG macro is not defined, the code between #else and #endif will be compiled, and the output will be “Debug mode disabled.

The #if defined directive allows you to create different branches of code execution based on the existence of specific macros or symbols, making your program more flexible and adaptable to different scenarios.

What is the difference between #ifdef and #if defined?

#ifdef and #if defined are both preprocessor directives in the C programming languages that are used for conditional compilation. While they serve a similar purpose, there is a slight difference between them.

The #ifdef directive checks if a macro or identifier is defined, and if it is, the code within the #ifdef block is compiled. On the other hand, the #if defined directive is used to evaluate a condition based on whether a macro or identifier is defined, and if the condition is true, the code within the #if defined block is compiled.

In practical terms, the only difference is in the syntax. With #ifdef, you only need to specify the macro or identifier directly after it, like this:

#ifdef MACRO_NAME
    // Code to be compiled if MACRO_NAME is defined
#endif

With #if defined, you need to use the defined operator along with the macro or identifier you want to check, like this:

#if defined(MACRO_NAME)
    // Code to be compiled if MACRO_NAME is defined
#endif

Both directives offer conditional compilation based on the status of macros or identifiers, allowing you to selectively include or exclude code sections during the compilation process. Choose the one that best fits your preference and coding style.

When using #if defined, you can combine multiple conditions using logical operators such as && (AND) and || (OR).

Here’s an example to demonstrate the usage of logical operators in #if defined:

#include <stdio.h>

#define DEBUG
#define TESTING

int main() 
{
#if defined(DEBUG) && defined(TESTING)
    printf("Debug mode enabled and testing is in progress.\n");
#elif defined(DEBUG) || defined(TESTING)
    printf("Debug mode enabled or testing is in progress.\n");
#else
    printf("Neither debug mode nor testing is active.\n");
#endif

    return 0;
}

In this example, the DEBUG and TESTING macros are defined using the #define directive. The #if defined(DEBUG) && defined(TESTING) condition checks if both DEBUG and TESTING macros are defined, and if the condition is true, the code between #if and #elif is compiled. The output will be “Debug mode enabled and testing is in progress.

If either DEBUG or TESTING macro is defined, the #if defined(DEBUG) || defined(TESTING) condition will be true, and the code between #elif and #else will be compiled. The output will be “Debug mode enabled or testing is in progress.

If neither DEBUG nor TESTING macro is defined, the code between #else and #endif will be compiled, and the output will be “Neither debug mode nor testing is active.

By using logical operators in conjunction with #if defined, you can create more complex conditions for conditional compilation based on the existence of multiple macros or identifiers.

Conditional compilation can be extremely useful in many scenarios. For example, you can use it to include platform-specific code, define feature flags, or enable debugging statements in development builds while excluding them in production builds. Conditional compilation allows developers to write flexible and customized code for different scenarios without cluttering the codebase with unnecessary statements. It enhances code readability, maintainability, and portability.

Miscellaneous Preprocessor Directives

Miscellaneous Directives are two types and they are not commonly used in the program.

#undef

The #undef directive in C is used to undefine a defined macro. It is commonly used to remove preprocessor definitions or macros that were previously defined using the #define directive.

Here is an example:

#include <stdio.h>

#define PI 3.14159

int main() 
{
    printf("The value of PI: %f\n", PI);
    
    #undef PI
    
    printf("After undefining PI:\n");
    printf("The value of PI: %f\n", PI); // This will cause a compilation error
    
    return 0;
}

In the above example, we define the macro PI with a value of 3.14159. After that, we use the #undef directive to remove the definition of PI. Therefore, when we try to access PI again, it will result in a compilation error because it is no longer defined.

#pragma

In C programming, #pragma is a preprocessor directive that provides specific instructions to the compiler. It is used to enable or disable specific features, control optimization settings, or provide additional information to the compiler.

Here are a few common uses of #pragma in C:

  1. Pragma Once: This directive ensures that a header file is included only once in a source file, even if it is included multiple times. It helps prevent multiple definitions of variables, types, or functions.
#pragma once

// Header file content
  1. Pragma Message: This directive allows you to display custom warnings or informational messages during compilation. It can be useful for debugging or providing instructions to the developers.
#pragma message "Compiling custom message..."

// Rest of the code
  1. Pragma Pack: This directive controls the alignment and padding of structure members. It specifies the byte alignment for subsequent structure declarations.
#pragma pack(push, 1)
struct MyStructure 
{
    // Structure members
};
#pragma pack(pop)

// Rest of the code
  1. Pragma Inline: This directive suggests the compiler to inline a function, which means inserting the function’s code at each calling site, instead of invoking it as a separate function. It can improve performance in certain cases.
#pragma inline
int add(int a, int b) 
{
    return a + b;
}

// Rest of the code

Please note that the specific behavior and availability of #pragma directives may vary depending on the compiler you are using.

#error

#error is a preprocessor directive in C programming that generates a compilation error with a specified error message. This directive is typically used to provide informative error messages during the compilation process. When the compiler encounters #error, it terminates the compilation and displays the specified error message.

Here’s an example:

#if defined(WIN32)
    // Windows-specific code
#else
    #error "This code is only compatible with Windows"
#endif

In the above example, #error is used to generate an error message if the code is being compiled on a non-Windows platform. The compiler will display the error message “This code is only compatible with Windows” and terminate the compilation.

Using #error can be helpful in ensuring that your code is correctly compiled and executed on the desired platform, and it provides a clear indication of any compatibility issues that may arise.

#warning

The #warning directive in C is a preprocessor directive that allows you to generate warning messages during compilation. It is often used to display custom warning messages or reminders to developers. When the compiler encounters a #warning directive, it displays the specified warning message, but it does not stop the compilation process.

Here’s an example of how to use #warning:

#warning Remember to refactor this code before release.

// Rest of the code

In the above example, the #warning directive is used to display a reminder message to the developers. When the code is compiled, the compiler will display the warning message “Remember to refactor this code before release.

The #warning directive can be useful for highlighting areas of code that need attention or improvement, providing guidance to developers, or serving as reminders during the development process. It helps in maintaining code quality and reducing potential issues.

In our next article, we will see the difference between macro and inline functions in C programming. If you want to know the compilation process in C programming, you can read this article.

You can also read the below tutorials.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
STM32F7 Bootloader TutorialsRaspberry PI Pico Tutorials
STM32F103 Bootloader TutorialsRT-Thread RTOS Tutorials
Zephyr RTOS Tutorials – STM32Zephyr RTOS Tutorials – ESP32
AUTOSAR TutorialsUDS Protocol Tutorials
Product ReviewsSTM32 MikroC Bootloader Tutorial
VHDL Tutorials
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Table of Contents