This article provides a comprehensive overview of preprocessor directives in C programming language along with relevant examples.
C provides many features like structures, unions and pointers. Another unique feature of the C language is the preprocessor. The C preprocessor provides several tools that are not present in other high-level languages.
The programmer can use these tools to make his program easy to read, easy to modify, portable and more efficient. The preprocessor is a program that processes the source code before it passes through the compiler.
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 are taken and then the source program is handed over to the compiler.
All the preprocessor directives follow special syntax rules that are different from the normal C syntax. Every preprocessor directive begins with the symbol # and is followed by the respective preprocessor directive. The preprocessor directives are divided into three categories. They are:
- Macro Substitution Directives
- File Inclusion Directives
- Compiler Control Directives
Contents
Macro Substitution Directives
Macro substitution is a process where an identifier in a program is replaced by a predefined string composed of one or more tokens. The preprocessor accomplishes this task under the direction of #define statement. This statement, usually known as a macro definition takes the following form:
If this statement is included in the program at the beginning, then the preprocessor replaces every occurrence of the identifier in the source code by the string.
Note: Care should be taken that there is no space between the # and the word define. Also there should be atleast a single space between #define and the identifier and between the identifier and the string. Also, there will be no semi-colon at the end of the statement.
There are different forms of macro substitution. The most common are:
- Simple macro substitution
- Argumented macro substitution
- Nested macro substitution
Simple Macro Substitution
The simple macro substitutions are generally used for declaring constants in a C program. Some valid examples for simple macro substitution are:
Whenever the preprocessor comes across the simple macros, the identifier will be replaced with the corresponding string. For example, in a C program, all the occurrences of PI will be replaced with 3.1412.
Argumented Macro Substitution
The preprocessor allows us to define more complex and more useful form of substitutions. The Argumented macro substitution takes the following form:
Care should be taken that there is no space between the identifier and the left parentheses. The identifiers arg1, arg2, …. , argn are the formal macro arguments that are analogous to the formal arguments in a function definition.
In the program, the occurrence of a macro with arguments is known as a macro call. When a macro is called, the preprocessor substitutes the string, replacing the formal parameters with actual parameters.
For example, if the Argumented macro is declared as shown below:
and the macro is called as shown below:
Then the preprocessor will expand the above statement as:
Nested Macro Substitution
We can use one macro inside the definition of another macro. Such macros are known as nested macros. Example for a nested macro is shown below:
File Inclusion Directives
The external files containing functions or macro definitions can be linked with our program so that there is no need to write the functions and macro definitions again. This can be achieved by using the #include directive. The syntax for this directive is as shown below:
We can use either of the above statements to link our program with other files. If the filename is included in double quotes, the file is searched in the local directory. If the filename is included in angular brackets, then the file is searched in the standard directories.
Compiler Control Directives
Following are the compiler control directives:
Directive | Purpose |
#ifdef | Test for a macro definition |
#endif | Specifies the end of #if |
#ifndef | Tests whether a macro is not defined |
#if | Test a compile-time condition |
#else | Specifies alternative when #if fails |
These compiler control directives are used in different situations. They are:
Situation 1
You have included a file containing some macro definitions. It is not known whether a certain macro has been defined in that header file. However, you want to be certain that the macro is defined.
This situation refers to the conditional definition of a macro. We want to ensure that the macro TEST is always defined, irrespective of whether it has been defined in the header file or not. This can be achieved as follows:
#include”DEFINE.H”
#ifndef TEST
#define TEST 1
#endif
DEFINE.H is the header that is supposed to contain the definition of TEST macro. The directive #ifndef TEST searches the definition of TEST in the header file and if it is not defined, then all the lines between the #ifndef and the corresponding #endif directive are executed in the program.
Situation 2
Suppose a customer has two different types of computers and you are required to write a program that will run on both the systems. You want to use the same program, although a certain lines of code must be different for each system.
The main concern here is to make the program portable. This can be achieved as shown below:
#ifdef IBM_PC
{
----
----
}
#else
{
----
----
}
#endif
If we want to run the program on a IBM PC, we include the directive #define IBM_PC, otherwise we won’t.
Situation 3
You are developing a program for selling in the open market. Some customers may insist on having certain additional features. However, you would like to have a single program that would satisfy both types of customers.
This situation is similar to the above situation and therefore the control directives take the following form:
#ifdef ABC
Group-A lines
#else
Group-B lines
#endif
Group-A lines are included if the customer ABC is defined. Otherwise, Group-B lines are included.
Situation 4
Suppose if you want to test a large program, you would like to include print calls in the program in certain places to display intermediate results and messages in order to trace the flow of execution and errors.
For this purpose we can use #if and #else directive as shown below:
#if constant expression
{
Statement 1;
Statement 2;
-----
}
else
{
Statement 1;
Statement 2;
-----
}
#endif
ANSI Preprocessor Directives
The ANSI committee has added some more preprocessor directives to the existing list. They are:
Directive | Purpose |
#elif | Provides alternative test facility |
#pragma | Specifies compiler instructions |
#error | Stops compilation when an error occurs |
#elif Directive
The #elif directive enables us to establish an “if…else…if” sequence for testing multiple conditions. The syntax is as shown below:
#if expr1
Stmts;
#elif expr2
Stmts;
#elif expr3
Stmts;
#endif
#pragma Directive
The #pragma directive is an implementation oriented directive that allows the user to specify various instructions to be given to the compiler. Syntax is as follows:
#pragma name
Where name is the name of the pragma we want. For example, under Microsoft C, #pragma loop_opt(on) causes loop optimization to be performed.
#error Directive
The #error directive is used to produce diagnostic messages during debugging. The general format is:
#error error-message
When the #error directive is encountered by the compiler, it displays the error message and terminates the program.
Example:
#if !defined(FILE_G)
#error NO GRAPHICS FILE
#endif
Preprocessor Operations
Stringizing Operator #
ANSI C provides an operator # called stringizing operator to be used in the definition of macro functions. This operator converts a formal argument into a string. For example, if the macro is defined as follows:
#define sum(xy) printf(#xy “ = %f \n”, xy)
and somewhere in the program the statement is written as: sum(a+b); then the preprocessor converts this line as shown below:
printf(“a+b” “ = %f \n”, a+b);
Token Pasting Operator ##
The token pasting operator ## defined by ANSI enables us to combine two tokens within a macro definition to form a single token.
Suryateja Pericherla, at present is a Research Scholar (full-time Ph.D.) in the Dept. of Computer Science & Systems Engineering at Andhra University, Visakhapatnam. Previously worked as an Associate Professor in the Dept. of CSE at Vishnu Institute of Technology, India.
He has 11+ years of teaching experience and is an individual researcher whose research interests are Cloud Computing, Internet of Things, Computer Security, Network Security and Blockchain.
He is a member of professional societies like IEEE, ACM, CSI and ISCA. He published several research papers which are indexed by SCIE, WoS, Scopus, Springer and others.
I like the article
Thanks for the terrific guide