Difference between Preprocessor Directives and Macro

Preprocessor Directives

Introduction

    • The C pre-processor is not part of the compiler but is a separate step in the compilation process.
    • The syntax of the pre-processor is different from the syntax of the rest of the C program in several respects.

Definition

    • Preprocessor directives in C are instructions that are processed by the preprocessor before the actual compilation of code begins.
    • C pre-processor is just a text substitution tool, which filters our source code before it is compiled.
    • Preprocessor directives allow us to control how our program is compiled, making them very useful for defining constants, macros, conditional compilation, and improving code portability.

Features/Characteristics

    • The pre-processor is a translation phase that is applied to the source code/program before the compiler gets its hands on it.
    • They provide a way to include files, define constants, macros, and control conditional compilation.
    • It is a very powerful tool for the programmer.
    • All pre-processor directives or commands are indicated by a # symbol at the beginning of a line.

Types of Pre-processor Directives

    • The pre-processor directives perform textual substitutions during the compilation time in our source code. On this basis, they are of three types –
      • File Inclusion: Inserting the contents of another file into our source file, as if we had typed it all in there. For example – #include.
      • Macro Substitution: Replacing instances of one piece of text with another. For example – #define.
      • Conditional Compilation: Depending on various circumstances, certain parts of the source code are seen or not seen by the compiler at all. For example – #if, #if-#else, etc.

Advantages

    • The preprocessor makes programs easier to develop, read and modify.
    • The preprocessor makes C code portable between different machine architectures & customizes the language.
    • The preprocessor allows us to customize the language.

Lists of Pre-processor Directives

#include 
    • The #include directive is used to include the contents of a file (usually a header file) into the current source file for their definition for the compiler.
    • Syntax
#include <filename>   // Includes Standard library file
#include “filename”   // Includes User-defined file
    • Example
#include <stdio.h>     // Include the standard I/O library
#include “myheader.h”  // Include a user-defined header file
#define
    • The #define directive is used to define macros or symbolic constants used in the program.
    • It can also be used to define/create function-like macros.
    • In #define preprocessor, the literal is substituted by literal value/constant at every occurrence, before compilation of the program. 
    • No semicolon (;) needs to be placed as the delimiter at the end of a # define line.
    • Syntax
#define <CONSTANT_NAME/LITERAL> <Replacement-Value>
#define MACRO_NAME(arguments) (code)     
(Where literal is the identifier which is replaced with replacement-value in the program.)
    • For Example –   
#define PI 3.14159                 // Define a constant for PI
#define MAXSIZE 20             // Define a constant for MAXSIZE
#define SQUARE(x) ((x) * (x))      // Define a function-like macro to calculate square

(Here, the C preprocessor simply searches through the C code before it is compiled and replaces every instance of PI, MAXSIZE, and SQUARE(x) with 3.14159, 20, and x*x respectively.)

Here, the preprocessor replaces all occurrences of PI with 3.14159 and MAXSIZE with 20. SQUARE(x) is a macro that calculates the square of x.

#undef 
    • The #undef directive is used to undefine a previously defined macro.
    • Once a macro is undefined, it is no longer available.
    • Syntax
#undef MACRO_NAME
    • Example
#define MAX 100
#undef MAX      // Remove the definition of MAX

Here, after #undef MAX, MAX can no longer be used in the code.
#ifdef and #ifndef 
    • Both Macros check if a macro is defined(#ifdef) or not defined(#ifndef).
    • #ifdef: The code inside this block is compiled only if the specified macro is defined.
    • #ifndef: The code inside this block is compiled only if the specified macro is not defined.
    • Syntax
#ifdef MACRO_NAME
    // Code to include if MACRO_NAME is defined
#endif

#ifndef MACRO_NAME
    // Code to include if MACRO_NAME is not defined
#endif
    • Example
#define DEBUG
#ifdef DEBUG
    printf(“Debug mode is ON\n”);
#endif
#ifndef RELEASE
    printf(“Release mode is OFF\n”);
#endif

Here, the code inside #ifdef DEBUG is compiled if DEBUG is defined and the code inside #ifndef RELEASE is compiled if RELEASE is not defined as above.
#if, #elif, #else, and #endif 
    • These directives allow conditional compilation based on expressions.
    • Syntax
#if expression
    // Code to include, if expression is true
#elif expression
    // Code to include, if previous expressions were false and this is true
#else
    // Code to include, if all conditions are false
#endif
    • Example
#define VERSION 2

#if VERSION == 1
    printf(“Version 1\n”);
#elif VERSION == 2
    printf(“Version 2\n”);
#else
    printf(“Other Version\n”);
#endif

Here, the #if directive checks the condition VERSION == 1. If true, it includes the code inside it.
The #elif directive is used if the previous condition was false.
The #else directive is executed if all previous conditions are false.
The #endif directive marks the end of the conditional block.
#pragma 
    • The #pragma directive provides additional compiler-specific instructions.
    • It is used to control the behavior of the compiler, such as disabling warnings.
    • Syntax

#pragma directive-name

    • Example
#pragma message(“Compiling this file”)
Here, the above code will print a message “Compiling this file” during compilation. Pragmas are compiler-dependent and their effects vary between compilers.
#error 
  • The #error directive causes the preprocessor to generate an error message and halt the compilation.
  • Syntax
#error message
  • Example
#ifndef VERSION
#error “VERSION is not defined”
#endif

Here, If VERSION is not defined, this directive will display an error message during compilation and stop the process.
#line 
    • The #line directive changes the current line number and optionally the filename for debugging purposes in the preprocessor output.
    • It’s mainly used for debugging.
    • Syntax

#line number “filename”

    • Example
#line 100 “file1.c”
printf(“This is line 100 in file1.c\n”);

The #line directive changes the line number to 100 and the filename to “file1.c”, which will appear in error messages or debugging information.
__FILE__ and __LINE__ Predefined Macros
  • These are special predefined macros that give information about the current file and line number during compilation.
  • For example –
#include <stdio.h>

int main()
{
    printf(“Current file: %s\n”, __FILE__);   // Prints the current file name
    printf(“Current line: %d\n”, __LINE__);   // Prints the current line number
    return 0;
}

Macros

Introduction

    • Macros are symbolic constants and compile time functions.
    • Macros are string replacement techniques commonly used to define constants.

Definition

    • Macros in C is a way to define constants, functions, or code blocks that get substituted into the program at compile time. They are defined using the #define preprocessor directive.
    • A macro is a segment of code that is replaced by the value of a defined macro.
    • Macros are one of the Pre-processor directives available in C.

Syntax

The macro definition takes the following form :
#define identifier string

Here, #define is a type of macro/Pre-processor directives, the identifier is a macro name and the string a macro replacement.

    • To create/define a macro :
      • The above syntax statement is included in the program at the beginning(above main() with other header files), and then the pre-processor automatically replaces every occurrence of the identifier in the source code with the string value.
      • To create a macro, the keyword #define is written followed by the identifier name and a string value with at least one blank space between them (as in syntax).
      • The definition is macro is not terminated by a semicolon(;) symbol as a line terminator.
      • The string value may be any constant/expression while the identifier name must be a valid C name in capital letters.
      • We can also use macro defined in the system standard libraries such as  ‘abs’, which returns the absolute value of its parameter. Again, we can also create/define our version of a customized ABS macro similar to the system-defined macro but with a different identifier name such as ABS (system-defined is abs) to avoid conflicting with the existing name abs in the system standard library.

#define ABS(x)  ((x) < 0) ? – (x) : (x)

    • To Disable/Undefining a Macro :
      • A defined macro can be disabled/undefined, using the #undef statement.
      • Syntax :

#undef identifier(name)

      • This option is applied when we want to restrict the definition of macro only to a particular region/part of the program.

Example

#include<stdio.h>
#define MAX 200

It sets the value of identifier MAX to 200 and whenever we want to use 200 in our program we can put MAX instead of writing 200 which is later substituted by the compiler at the proper place with its value/constant. In other words, Whenever we want to use the value of MAX  in our program segment code we can simply write MAX instead of writing a lengthy value of MAX again and again. We can put/use multiple times MAX in our program code as per the need of the program.

Features/Characteristics

    • It is a pre-defined symbol with or without parameters.
    • We can create our own macros with the #define pre-processor directive.
    • When we use the macro, its value (with the actual parameters) is substituted at the location in the program where we use it.
    • We can create our own customized macro as per the logic/need of the program or can also use system-defined standard library macro in our program.
    • Macros are inline code, which is substituted at compile time.
    • All the macros are replaced with their definitions during compile time.
    • Macro substitution is a process where an identifier in a program is replaced by a predefined string by the pre-processor.
    • Macros can also accept parameters and return values. These macros are called Macro Functions.
    • The Main use of Macro is not to solve this type of program rather it is often used to create codes that can be compiled on different machines.
    • Macros work similarly as a function but with few differences
      • Macros are implemented as a textual substitution, therefore by this the performance of the program improves compared to functions
      • Recursive macros generally do not suggest to use of like functions.
      • Macros don’t care about the data type of their arguments. Hence macros are a good choice where we want to operate on reals, integers, or a mixture of the two.
      • Macros are generally fairly small.

      Types of Macro Substitutions

        • There are different types of macro substitution. These are:-

      (a) Simple Macro Substitution :

          • They are simple string replacements with defined identifiers.
          • They are also called ‘Object like Macro’.
          • They are widely used macro.
          • For example – 
        #define VAL  70
        #define MAX 200

        (b) Argumented Macro Substitution :

            • This type of Macro allows us to define more complex and more useful forms of replacements.
            • They are also called ‘Functions like Macro’ because they work like a function call.
            • Syntax :

        #define identifier(f1,f2,..,fn) string

            • For example
        #define CUBE(x) (x*x*x)
        #define SQUARE(Y) (Y*Y)
        The above macro statement can be used in the program in the following  format –
        volume=CUBE(side);
        then the pre-processor will expand the defined macro in the program at the proper place as –
        volume=(side*side*side);

        (c) Nested Macro Substitution :

            • Here, we can use one/more macros in the definition of another macro as per the logic/need of the program.
            • For example –
        #define SQUARE(x) (x*x)
        #define CUBE(SQUARE(x)*(x))
        Also,
        #define VAL 10
        #define NVAL (VAL +1)
        #define MAX(VAL , NVAL )  ((VAL >NVAL ) ? VAL :NVAL )

        Advantages

          • Macros can make long, ungainly pieces of code into short words.
          • It increases the execution speed of the code because there is no function call overhead.
          • They are used to avoid repetitive codes and some dependencies in the program.

        Disadvantage

          • Macro is used carefully, taking in mind the order of precedence of operators, especially arithmetic type.
          • Macros don’t show debugger results correctly or are less clear.

        Use of Macro

          • Constant Definition: Macros are used to define constants for values like PI.
          • Function-like Macros: Macros can be used to perform small, reusable operations like functions such as SQUARE(x) or MAX(a, b).
          • Conditional Compilation:  They are used to include/exclude parts of code in the program such as #ifdef, #endif, and similar macros.
          • Code Reusability: They are used to define frequently used code blocks like SWAP(a, b) to reduce redundancy.
          • Error Handling: Macro simplifies error checking and reporting.

        Loading

        Categories: C

        0 Comments

        Leave a Reply

        Your email address will not be published. Required fields are marked *

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