Table of Contents
Introduction
When we write a program, it is not always straight forward. We will always deal with some constants, some predefined conditions, functions etc. These predefined constants and functions are repeatedly used or called in various programs. One of the examples is reading the input from the standard input, i.e.; from keyboard requires some function which reads the key that user has entered or pressed. Rather than writing the function in each program to read the data that user has entered, we can write a standard / common function to read the data entered from the keyboard, i.e.; scanf (). This function will be kept in the standard input output library, which can be inserted in the program using preprocessor directive. We also call this preprocessor directive as header file. Whenever a compiler compiles the program, it replaces all the codes in the header files into the current program first and then compiles the program. Thus when the program calls the scanf function it gets the function definition from the header file. In this way, it helps to use the predefined function in all other programs.
Similarly, suppose in a program we have to increment the salary of the employees by 10%. This is a constant value by which we are going to increment the salary. But when we write a program, we will be hard coding this increment factor. Suppose there will be multiple places where we will be incrementing the values. This program might be used by several users or several times to increment the salary. Suppose what if this increment percentage changes? We need to change whole program, wherever the salary increment is done. Rather than doing this, if we have defined this increment percentage in a variable and mark it as constant, we can overcome the difficulty of changing the whole program when this percentage changes. But if we define a constant variable and use it in a program, when the compiler compiles the program, it will not replace the constant variable value at compile time. It will be replaced at runtime as a variable with memory address. This is a time consuming task while executing. If we have the facility to store the value in a constant variable and to replace the value of this variable as value itself rather than replacing it as variable with memory address, execution time will be very less. For this purpose we can use preprocessor directive.
Preprocessor Directives
Preprocessor directives are the text replacement tool, used to use in the program to replace the text by it value. It is different from variable. Whenever a variable is used in the program, compiler understands it as a value stored in some memory address. But when preprocessor directive is used in the program, the text or the name of the processor directive is not considered as some value or code in the memory. Compiler replaces them by its actual value in the program and then compiles the program.
For example, suppose we have to increment the salary by 10% and we have defined this constant as variable. Then:
const float SAL_INCRE= 0.1;
If we have any code in the program using above constant variable, then when compiler compiles the program, it will see the code as below:
intSal = intSal * SAL_INCRE; → Compiler will replace the value of SAL_INCRE at run time.
Suppose we have made this SAL_INCRE as preprocessor directive instead of constant variable, then compiler sees above formula as below:
intSal = intSal * 0.1;
That means, whenever preprocessor directive is used it replaces the name by its actual value at all the places it is used.
There are different types of preprocessor directives – constants, macros, conditions, functions etc. All the preprocessor directives begin with ‘#’ symbol, followed by preprocessor directive command name. For example,
#include <stdio.h>// includes header file into current program #include <string.h> // includes header file into current program #define SAL_INCRE 0.1 //defines constant value #define MAX_ARRAY_SIZE 15 // defines maximum array size #define SQUAR (x) (x*x) //defines the formula for finding the square of a number #ifndef MESSAGE // defines the value for MESSAGE if this macro is not defined, else it uses the old macro value #define MESSAGE "Preprocessor Directive Example" #endif
From above examples, we can understand that the preprocessor directives are helpful in
- Developing easy to develop, easy to read and easy to modify programs.
- Transporting the code between different machines.
#define
This preprocessor directive is used to define the constant values or some messages or some formula or any other macro substitutions.
#define PI 3.14 //defines constant value #define MAX_ARRAY_SIZE 15 // defines constant value which is used for maximum array size #define SQUAR (x) (x*x) //defines the formula for finding the square of a number #define TRUE TR // redefines TRUE as TR, in the program TR can be used instead of TRUE #define FALSE FL// redefines FALSE as FL, in the program FL can be used instead of FALSE
In the above examples, first two defines constant values. Third one is used to define a SQUARE function. Last two directives redefine TRUE as TR and FALSE as FL. These redefined names can be used in the program whenever we need to use TRUE or FALSE. This is useful when we have very length or difficult names in the program and this redefining the names will make the code simpler and help the developer to write the code fast.
#undef
This is used to undefine the values or formula or functions defined earlier. This is useful if we have to redefine the directives or macros. If we have to redefine the macro, then we have to first undefine the macro using undef and then redefine it using define.
#define MAX_ARRAY_SIZE 50 // defines maximum array size for the first time #undef MAX_ARRAY_SIZE // undefines the MAX_ARRAY_SIZE defined earlier #define MAX_ARRAY_SIZE 15 // redefines maximum array size
We can define the macro anywhere in a program. For example, we have to increment the salary of employees by 10% for some of the departments and for rest of the departments it needs to be incremented by 20%. We can define two different macros in this scenario. But using the same macro, we can first define salary increment as 10% and perform the calculations for those departments. After that we can redefine the value as 20% by undefining and defining the macro.
#define SAL_INCRE 0.1 //defines constant value //Perform the calculations for the employees with 10% increment #undef SAL_INCRE // undefines the SAL_INCRE defined earlier #define SAL_INCRE 0.2 // redefines maximum array size //Calculate the salary for rest of the employees
Header Files – #include
This preprocessor directive is used to include other files into the current program. Usually the files that are to be included will be saved with ‘.h’ extension. We include the files into current program using preprocessor directive as below:
#include <file_name.h> //Used for inbuilt header files
OR
#include "file_name.h" //Used for user defined header files #include <stdio.h>// includes header file into current program #include “string.h” // includes header file into current program
This preprocessor directive is also called as header files. This is because, for any program to get execute, we need to use some predefined functions and macros. They are all defined in the header files and they needs to be included in the file first. When the compiler compiles the program, it first searches for these header files in the system (usually these files will be in program directory itself; else they will be in usr/include/ folder).
When a header file is included in the program, it is written as first code below. When the compiler compiles the code, it sees the header file as second code below. That means it replaces the header file with its entire code.
C allows to include as many header files that user wants. Same header files can be included more than once. We have seen above that compiler will replace the header file by its content. Hence when the compiler compiles the program it sees same function name twice, thus results in error. C does not allow any two functions to have same function name. This can be avoided by defining the header files using #ifndef syntax. This preprocessor command first checks if the function or keyword is already defined, if not, it defines the function. Thus when same header file is included twice, it sees that the function is defined already and comes out. Hence the entire program gets only one header file.
#include <stdio.h> #include “test.h” void main(){ printf("Testing header files\n"); test(); }
// test.h #ifndef test #define test() printf("Within the header file\n") #endif
Sometimes there will be requirement to include different header files based on certain conditions. In such case we would be having #if preprocessor directive to include header files.
#if condition1 #include"condition1.h" #elif condition2 #include"condition2.h" #elif condition3 #include"condition3.h" #endif
But including header files using conditional preprocessor will be tedious as the number of condition grows. In addition, it creates problem in understanding the code and may cause repetition of the same header file. In order to overcome this problem, C provides computed includes, where we define a macro which includes the header file whichever is necessary for the program.
#define HEADER_FILE "header_file.h" //defining a macro for the header file #include HEADER_FILE // this will automatically detect which header file to be included and will include it in the program
#if..#endif
This is similar to if condition, but it is used to evaluate constant integer expression. Like If condition’s else and else if conditions, it also has #else and #elif respectively. All the #if macro should end with #endif.
#if A>B #define “A is greater than B" #endif
#if A>B #define "A is greater than B" #else #define "A is lesser than B" #endif
These are the simple conditional preprocessor directives. We can also have nested conditional preprocessor for conditional compilations.
#if A>B #define "A is greater than B" #else #if B>C #define “B is Greater than C” #else #define “C is Greater than A and B” #endif #endif
#if A>B #define "A is greater than B" #elif B>C #define "B is Greater than C" #else #define"C is Greater than A and B" #endif
Macros
Macros are the predefined set of codes which are executed automatically when it found in the program. There are predefined macros as well as user defined macros. Some of the predefined macros are listed below. These macros cannot be changed and returns predefined output.
User defined macros are those that are discussed above like #define, #if, #ifdef, #ifndef, #undef etc. these are used to define some rules and conditions that needs to be included in the program. These macros need not be limited to single line. We can have multiline macros as well as nested macros like #if..#elif..#endif.
Macros need not be simple names or tokens. It can also have parameters. For example, macro to find the square of a number is a parameterized macro. It accepts the number as parameter whose square has to be found.
#define SQUAR (x) (x*x) //defines the formula for finding the square of a number #define Factorial (N) (N*Factorial (N-1)) //macro to find the factorial of a number
Preprocessor Operators
Macros created in C are also composed of C codes, expressions and operators. It uses the same operators that are used by expressions in C, but it has different meaning when used as macros.
Multiline Continuation Macro (\)
In general scenario, macros or preprocessor directives are written in single line. But we can have very lengthy or multiline definitions/ commands. In such scenarios, we need to have some operator which tells the compiler that the macro is not limited to single line; but next line is also part of the same macro.
#include <stdio.h> #define displayEmpName (strName) \ printf ("Employee name is: %s\n", strName) // definition of macro is in next line void main(){ char *strEmpName = "Mathew"; displayEmpName(strEmpName); }
Stringize (#)
Macros or any preprocessor directives begin with ‘#’ symbol indicating the compiler that they are preprocessor directives. These macros can have parameters too. In order to access these parameters within the macro can be accessed using ‘#’ symbol. When these macro parameters are accessed within the macro using ‘#’, it replaces them by its string value.
#include <stdio.h> #define displayEmpName(strName) \ printf("Employee name is: " #strName) //accessing the macro parameter using stringize void main(){ displayEmpName("Robert"); // Double quote is also displayed }
Token Pasting (##)
This is used to combine two different macro parameters into one parameter.
#include <stdio.h> #define empSalary(n) printf("Salary of the employee"#n " is: %d\n", salary##n) // appends the employee number void main(){ int salary100 = 1000; int salary101= 2000; int salary102 = 3000; int salary103 = 4000; empSalary(100);//employee numbers are passed as parameters empSalary(101); empSalary(102); empSalary(103); }
In this example, the macro is defined to print the salary of employees. The first #n in the macro is used to append the employee if to the employee. In the second case when ‘##n’ is used, it appends the employee id to the ‘salary’. When this is used in the printf statement it returns the value represented by the variable – salary100, salary200, salary300 etc
Defined ()
This is the operator used to check if the macro is defined already. If defined, it returns TRUE or non-zero value. If it is not defined, then it returns FALSE or zero. This operator is usually used in #if condition.
#include <stdio.h> #if !defined (SAL_INCREMENT) // verifies if the macro SAL_INCREMENT is already defined #define SAL_INCREMENT 0.1 //if not defined, set the value #endif void main(){ int intSal = 10000; printf("Salary before Increment = %d\n", intSal); printf("Incremented Salary = %f", intSal + (intSal *SAL_INCREMENT));// increments the salary }
Summary
Different types of preprocessor directives are :
Different preprocessor directive syntaxes are :