0% found this document useful (0 votes)
2 views28 pages

The Preprocessor

The document provides an in-depth explanation of the preprocessor in programming, detailing its role in processing source code before compilation through directives. It categorizes directives into macro substitution, file inclusion, and compiler control, while explaining syntax rules and providing examples for each category. Additionally, it discusses the importance of conditional compilation and the use of include guards to prevent multiple inclusions in header files.

Uploaded by

Prince Raj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views28 pages

The Preprocessor

The document provides an in-depth explanation of the preprocessor in programming, detailing its role in processing source code before compilation through directives. It categorizes directives into macro substitution, file inclusion, and compiler control, while explaining syntax rules and providing examples for each category. Additionally, it discusses the importance of conditional compilation and the use of include guards to prevent multiple inclusions in header files.

Uploaded by

Prince Raj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 28

THE PREPROCESSOR

Definition: The preprocessor is a program that processes the source code


before it passes through the compiler. It operates under the control of what
is known as preprocessor command lines or directives. Preprocessor
directives are placed in the source program before the main line. Before the
source code passes through the compiler, it is examined by the
preprocessor for any preprocessor directives. If there are any , appropriate
actions ( as per the directives) are taken and then the source program is
handed over to the compiler.

Syntax Rules for preprocessor directives :


They all begin with the symbol # in column one and do not require a
semicolon at the end.

List of directives :

All directives can be divided into three categories :


1. Macro substitution directives
2. File inclusion directives
3. Compiler control directives

Macro substitution :
Macro substitution is a process where a macro in the program is replaced
by numbers or expressions, or actual strings.

Syntax:

#define macro_name value

where:

macro_name → The name of the macro (a valid C identifier).

value → can be a number or an expression, or a string.

Note: If the above statement is included in the program at the beginning,


then all occurrences of the identifier are replaced with the value specified.

- There must be at least one blank space between the identifier and value.

There are different forms of macro substitutions:


1. Simple macro substitution
2. Argumented macro substitution
3. Nested macro substitution

Simple Macro substitution:


These are basically used to define constants. For example,

#define COUNT 100


#define PI 3.14
#define CAPITAL “DELHI”

All the occurrences of the above macros will be replaced by their respective
values.

Exception: Macro inside a string (quotation marks) does not get replaced.
For example,
#define M 5
total = M * value;
printf (“M = %d\n”, M);

These two lines would be changed during preprocessing as follows:

total = 5 * value;
printf(“M = %d*\n”, 5);

Note: It is a convention to write all macros in capital letters to identify


them as symbolic constants.

A macro definition can include expressions as well. For example,

#define AREA 5 * 23.5


#define SIZE sizeof(int) * 4

But care should be taken to prevent an unexpected order of


evaluation. For example,

#define D 45 - 22
#define A 78 + 32

Consider the evaluation of the equation

ratio = D/A;

The result of the preprocessor ‘s substitution for D and A is:

ratio = 45 - 22 / 78 + 32;

This is certainly not what we wanted. What we wanted was

ratio = (45 - 22) / (78 + 32);


So, this can be obtained by using parentheses around the strings as shown
below:

#define D (45 - 22)


#define A (78 + 32)

Note: It is a wise practice to use parentheses for expressions used in


macro definition.

The preprocessor performs a literal text substitution whenever the


defined name occurs. That's why we cannot use a semicolon to
terminate the #define statement.

More examples:
#define TEST if(x>y)
#define AND
#define PRINT printf(“Good.\n”);

Now if write as follows in our source code:

TEST AND PRINT

The preprocessor would translate the above line to:


if(x>y) printf(“Good\n”);
Macros with arguments:

Syntax:
#define MACRO_NAME(parameter1, parameter2, ...) value

Definition of macro call: The usage of macros with arguments in code is


known as a macro call

Examples:
#define CUBE(x) ( (x) *(x) *(x) )
In our source code if we have,

volume = CUBE(side);

This will be expanded by preprocessor as :


volume = ( (side) *(side) *(side) );

If the statement is like:

volume = CUBE (a+b);

This will expand to:

volume = ( (a+b) *(a+b) *(a+b) );

More examples:

#define PRINT(variable, format) printf (“variable = %format\n”,


variable)

can be called-in by

PRINT (price *quantity, f);

The preprocessor will expand it as follows:

printf (“price * quantity = %f\n”, price * quantity);

Nesting of macros:
Placement of one macro in the definition of another macro
Examples:
If there is a statement as

power = SIXTH(x);

In our source code, then it will be expanded as follows:

(CUBE(x) * CUBE(x))

This will be further expanded to:

( ( SQUARE (x) * (x) ) * ( SQUARE (x) * (x) ) )

Since SQUARE (x) is still a macro, it is further expanded to

( ( ( (x) *(x) ) *(x) )*( ( (x) *(x) ) *(x) ) )

which is finally evaluated as x6.


Macros can also be used as parameters of other macros. For
example,

#define MAX(M, N) ( ( ( M) > (N) ) ? (M) : (N) )

Macro calls can be nested

#define HALF(x) ( ( x) / 2.0)


#define Y HALF(HALF(x) )

So now if we use the macro, Y in our source code


printf(“Y: % .2f”, Y);

then this will result in a nested macro call


HALF(HALF(x))

It will expand as follows:

HALF(HALF(x)) → HALF((x) / 2.0) → ((x) / 2.0) / 2.0

Again, given the definition of MAX(a, b)

#define MAX(a, b) ( ( ( a) > (b) ) ? (a) : (b) )

we can use the following nested call to give the maximum of three values x,
y, and z:

MAX(x, MAX(y, z) )

This expands as:

MAX(x, ((y) > (z) ? (y) : (z)))

Here, MAX(y, z) is evaluated first, and its result is then used as an


argument to MAX(x, result).

Undefining a macro:

#undef macro_name ;

This is useful when we want to restrict the definition only to a particular part
of the program.

Example:
#include <stdio.h>

#define VALUE 100

int main() {
printf("Before #undef, VALUE: %d\n", VALUE);

#undef VALUE

printf("After #undef, VALUE: %d\n", VALUE);

return 0;
}
Output:
error: 'VALUE' undeclared (first use in this function)
11 | printf("After #undef, VALUE: %d\n", VALUE);

File inclusion
An external file containing functions or macro definitions can be included as
a part of a program so that we need not rewrite those functions or macro
definitions. This is achieved by the preprocessor directive

#include “filename”

where filename is the name of the file containing the required definitions or
functions.

At this point, the preprocessor inserts the entire contents of filename into
the source code of the program.

When the filename is included within the double quotation marks, the
search for the file is made first in the current directory and then in the
standard directories.
Example:
Create a Header File (myheader.h)
This file contains function declarations.

myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H
void greet();
int add(int a, int b);
#endif

Create a Separate .c File (myfunctions.c) This file contains the function


definitions.

myfunctions.c
#include <stdio.h>
#include "myheader.h"

void greet() {
printf("Hello, Welcome to Turbo\n");
}

int add(int a, int b) {


return a + b;
}

Create the Main Program (main.c)


This file will include the header file and call functions from myfunctions.c.

main.c
#include <stdio.h>
#include "myheader.h"

int main() {
greet();
int sum = add(10, 20);
printf("Sum: %d\n", sum);

return 0;
}
Output:
Hello, Welcome to Turbo
Sum: 30

Alternatively, this directive can take the form

#include<filename>

without double quotation marks. In this case, the file is searched only in
the standard directories.

Nesting of included files is allowed. That is, an included file can


include other files. However, a file cannot include itself.

Example:
Case 1: With Include Guards (Correct Way)
File1.h
#ifndef FILE1_H
#define FILE1_H
#include<stdio.h>
#include "file2.h"

void func1() {
printf("This is func1 in file1.h\n");
func2();
}

#endif
File2.h
#ifndef FILE2_H
#define FILE2_H

#include <stdio.h>

void func2() {
printf("This is func2 in file2.h\n");
}

#endif

main.c
#include "file1.h"
#include "file2.h"

int main() {
func1();
return 0;
}

Expanded Code After Preprocessing

Contents of stdio.h included


void func2() {
printf("This is func2 in file2.h\n");
}

// From file1.h
void func1() {
printf("This is func1 in file1.h\n");
func2();
}

int main() {
func1();
return 0;
}

Everything compiles fine because:


 FILE2_H prevents file2.h from being included twice.
 func2() is defined only once

Case 2: Without Include Guards (Problematic)


File1.h
#include "file2.h"
#include<stdio.h>
void func1() {
printf("This is func1 in file1.h\n");
func2();
}

File2.h (No guards)


#include <stdio.h>

void func2() {
printf("This is func2 in file2.h\n");
}

main.c
#include "file1.h"
#include "file2.h"

int main() {
func1();
return 0;
}

Expanded Code After Preprocessing


void func2() {
printf("This is func2 in file2.h\n");
}

void func1() {
printf("This is func1 in file1.h\n");
func2();
}

void func2() {
printf("This is func2 in file2.h\n");
}

int main() {
func1();
return 0;
}

Compilation Error:
error: redefinition of ‘func2’

Because:
 func2() is defined twice
 C does not allow a function to be defined more than once

Note: If an included file is not found, an error is reported and


compilation is terminated.

We can include .c files in our program using #include "filename.c". This is


useful when we want to split our code into multiple files but don’t want to
create a separate object file manually.

When we write a large program, it's a good practice to split the code into
multiple files for better organization. Normally, when we split code into
multiple .c files, we need to compile each .c file separately into an
object file (.obj ) and then link them together to create the final
executable.

However, including a .c file using #include "filename.c" directly


in another .c file makes the compiler treat it as if all the code is written in
a single file. This means:

● We don’t have to manually compile multiple .c files separately.


● The included .c file is compiled along with the main file in one
go.

When we include a .c file inside main.c using #include , the compiler treats
it as if the contents of the .c file were copied directly into main.c. This
means:

- No separate compilation is needed.

- No explicit linking step is required.

- Only main.c needs to be compiled and run.

Example:

Create stats.c (with a Macro Definition)


#include <stdio.h>

#define MAX(a, b) ((a) > (b) ? (a) : (b))

int sum(int a, int b) {


return a + b;
}

float average(int a, int b) {


return sum(a, b) / 2.0;
}

Create main.c
#include <stdio.h>
#include "stats.c"

int main() {
int x = 10, y = 20;

printf("Sum: %d\n", sum(x, y));


printf("Average: %.2f\n", average (x, y));

printf("Max: %d\n", MAX(x, y));

return 0;
}

Compiler Control Directives

Need for Compiler control directives :

1. Conditional Compilation
Allows selective compilation of code based on specific conditions.

Example: Enabling debugging code only in development.

#ifdef DEBUG
printf("Debug mode is ON\n");
#endif

2. Preventing Multiple Inclusions


Ensures a header file is included only once, preventing redefinition errors.
Example :
#ifndef HEADER_H
#define HEADER_H
// Code inside header file
#endif

Definition: Conditional compilation directives in C are preprocessor


directives that enable the inclusion or exclusion of specific code segments
based on certain conditions. These directives are evaluated during the
preprocessing phase, before the actual compilation of the program.

Here are the key conditional compilation directives in C:

#if: This directive tests if a certain condition is true. If the condition


evaluates to true, the compiler includes the code between #if and the next
#endif or #else, or #elif directive.

#ifdef: This checks if a macro is defined. If the macro is defined, the code
following #ifdef up to #endif, or #else, or #elif is compiled.

#ifndef: Opposite of #ifdef. It checks if a macro is not defined. If the macro


is not defined, the code following #ifndef is compiled.

#else: Used with #if, #ifdef, or #ifndef. If the preceding condition is false,
the compiler includes the code following #else.

#elif: Short for “else if”. Allows for multiple conditional expressions. If the
preceding #if, #ifdef, or #ifndef is false, and the condition in #elif is true, the
code following #elif is compiled.

#endif: Marks the end of a conditional compilation block started by #if,


#ifdef, #ifndef, #else, or #elif.

Syntax for #if :


#if condition
// Code to compile if condition is
true
#endif
Explanation:

● #if evaluates the given condition (which is usually a constant


expression).
● If the condition evaluates to true (non-zero), the code block following
#if is included in the program.
● If the condition evaluates to false (zero), the code block is excluded
from the program.

Example :
#include <stdio.h>

#define NUM 10

int main() {
#if NUM > 5

printf("NUM is greater than 5\n");

#endif

return 0;
}

Preprocessed Code (After Preprocessing):


#include <stdio.h>

int main() {
printf("NUM is greater than 5\n");

return 0;
}
Output: NUM is greater than 5

Syntax for #ifndef:


#ifndef MACRO_NAME

// Code to be included if MACRO_NAME is not defined

#endif

Explanation:
● #ifndef stands for "if not defined".
● It checks whether a macro is not defined before including the code
following it.
● If the macro is not defined, the code block after #ifndef will be
included in the program.
● If the macro is defined, the code block will be skipped.

Example :
#include <stdio.h>

#ifndef MY_MACRO

#define MY_MACRO 10

#endif

int main() {

printf("MY_MACRO = %d\n", MY_MACRO);

return 0;
}

Preprocessed Code (After Preprocessing):


#include <stdio.h>
int main() {
printf("MY_MACRO = %d\n", 10);
return 0;
}
Output: MY_MACRO = 10

Note: #ifndef, #define, and #endif are removed before compilation.

Syntax for #ifdef:


#ifdef MACRO_NAME

//Code to be included if MACRO_NAME is defined

#endif

Explanation:
● #ifdef stands for "if defined." It checks whether the macro,
MACRO_NAME has been previously defined using #define.
● If the macro is defined, the code block inside the #ifdef will be
included in the program.
● If the macro is not defined, the code block inside the #ifdef will be
ignored by the preprocessor.

Example:
#include <stdio.h>

#define NUM 10

int main() {
#ifdef NUM

printf("NUM is defined with value: %d\n", NUM);

#endif
return 0;
}

Preprocessed Code (After Preprocessing):


#include <stdio.h>

int main() {
printf("NUM is defined with value: %d\n", 10);
return 0;
}
Output: NUM is defined with value: 10

Syntax of #elif:

Syntax with #else:

#if CONDITION_1

// Code if CONDITION_1 is true


#elif CONDITION_2

// Code if CONDITION_1 is false


and CONDITION_2 is true

#elif CONDITION_3

// Code if CONDITION_1 and


CONDITION_2 are false and
CONDITION_3 is true

#else

// Code block if all conditions are


false

#endif

Key Points:

● #elif is used after an #if or another #elif to provide an


additional condition.
● #elif provides alternative conditions after the initial #if, checking if
specific conditions match. It allows the program to select one of the
code blocks to include based on the value of the defined macro.
● Multiple #elif statements allow checking several conditions in
sequence.

Example:
#include <stdio.h>

#define NUM 10

int main() {
#if NUM > 15
printf("NUM greater than15\n");
#elif NUM == 10
printf("NUM is equal to 10\n");
#else
printf("NUM is less than 10\n");
#endif

return 0;
}

Explanation:
● We define NUM as 10.
● The #if condition checks if NUM is greater than 15. Since it’s not, it
moves to the next condition.
● The #elif condition checks if NUM is equal to 10. This is true, so it
prints "NUM is equal to 10".
● If neither condition was true, the #else would be executed, but in this
case, it is skipped.

Preprocessed Code Output (What remains after preprocessing):

#include <stdio.h>

int main() {

printf("NUM is equal to 10\n");

return 0;
}

Output:

NUM is equal to 10
Syntax of #else
The #else directive is used with #if, #ifdef, #elif or #ifndef to specify an
alternative block of code when the condition specified with any of these is
false.

#if CONDITION
// Code if CONDITION is true
#else
// Code if CONDITION is false
#endif

Example:
#include <stdio.h>

#define VALUE 10

int main() {
#if VALUE > 5
printf("VALUE greater than 5\n");
#else
printf("VALUE is 5 or less\n");
#endif

return 0;
}
Preprocessed Code (After Preprocessing):

#include <stdio.h>
int main() {
printf("VALUE greater than 5\n");
return 0;
}

Output: VALUE greater than 5


Example Using #ifdef
#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;
}

Preprocessed Code (After Preprocessing)

#include <stdio.h>
int main() {
printf("Feature is enabled!\n");
return 0;
}

Output:

Feature is enabled!

If we comment out #define FEATURE_ENABLED, the output will be:


Feature is disabled!
Example Using #ifndef
#include <stdio.h>

#define CONFIG

int main() {
#ifndef CONFIG
printf("CONFIG is not defined!\n");
#else
printf("CONFIG is defined!\n");
#endif

return 0;
}

Preprocessed Code (After Preprocessing)

#include <stdio.h>

#define CONFIG

int main() {

printf("CONFIG is defined!\n");

return 0;
}

Expected Output (when CONFIG is defined):


CONFIG is defined!

Expected Output (when CONFIG is not defined):


CONFIG is not defined!
Example Using #elif and #else
#include <stdio.h>

#define VALUE 20

int main() {

#if VALUE < 10

printf("VALUE is less than 10\n");

#elif VALUE >= 10 && VALUE <= 30

printf("VALUE is between 10 and 30\n");

#else

printf("VALUE is greater than 30\n");

#endif

return 0;
}
Preprocessed Code (After Preprocessing):

#include <stdio.h>
int main() {
printf("VALUE is between 10 and 30\n");
return 0;
}
Expected Output:
VALUE is between 10 and 30

If we change VALUE to 5, the output will be:


VALUE is less than 10

If we set VALUE to 35, the output will be:


VALUE is greater than 30

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy