Introduction
About my this article, in this you all can get knowledge about preprocessor directives and pragmas. The preprocessor directives are used to help the programmer to make his/her program readable as well as meaningful. It is not compulsory to know the preprocessor directives, because one may write his programs without using any directives. But with the help of preprocessor we can easily change our source program even if we are working in different environments. Thus if we use any preprocessor directives then it means that the preprocessor has to perform specific actions, such as replace a lengthy string into shorter one, or ignore some portion of the source program, or insert the contents of other files into the source file etc.
Preprocessor Directives
The C preprocessor is a program that manipulates the source program before this program is passed to the compiler. A preprocessor directive is used to instruct the C preprocessor to perform a specific action in the source program before its compilation. Therefore the preprocessor directives are also known as preprocessor commands. This software is always included in a C package along with the standard C library functions. The C compiler automatically invokes the preprocessor in its first pass compilation either you are using any preprocessor directives or not. But the user can also invoke the preprocessor to manipulate the source program without its compilation.
The C preprocessor has following directives:
- #define #ifdef
- #elif #ifndef
- #else #include
- #endif #line
- #if #undef
Each of these preprocessor directives begins with a special symbol, #. The number sign (#) must be first non-white space character on the line containing the directive; white-space characters can appear between the number sign and the first letter of the directive. As stated earlier, the C preprocessor modifies the C source program according to the directives used in the program. It does not mean that the C preprocessor modifies the original file, rather it creates a new file that contains the processed version of the program. This new file is then submitted to the compiler.
The C preprocessor directives are divided into four categories:
- Macro Substitution
- File inclusion
- Conditional compilation
- Line control
Macro Substitution
Before the discussion of the preprocessor directives that supports macros, we must know, what is a macro? In earlier chapters, we have used identifiers to represent constants, variables, arrays, functions, structures and so on. But if an identifier is used to represent the statement or expression we call it macro. Generally we have two types of macros:
- Object–like macros
- Function–like macros
Object–like macros do not take any argument whereas the function-like macros take arguments, just like functions. Now we discuss these macros in some details.
Object-Like Macros
C preprocessor provides #define preprocessor directive to support macro facility. The syntax of #define directive is as:
#define <identifiername>
Here the #define directive substitutes the substitutiontext wherever our source program encounters the identifiername. For example, if the directive
#define PI 3.142857
is included in the program, then in all lines following the definition, the symbol PI is replaced by the number 3.142857.
Program-abc.c illustrates this concept:
/* Program - abc.c */
#include
#define PI 3.142857
main()
{
float radius, area, circum;
printf("\nEnter the radius of a circle : ");
scanf("%f", &radius);
area = PI * radius * radius;
circum = 2 * PI * radius;
printf("\nThe area of a circle is : %f", area);
printf("\nThe circumference of the a circle is : %f", circum);
}
The output of this program is as….
Enter the radius of a circle : 40
The area of a circle is : 5028.571289
The circumference of the a circle is : 251.428558
In this program, PI is a macro name and 3.142857 is its substitution text argument. This program would not be compiled directly, rather than it firstly invokes the preprocessor and the preprocessor substitutes the every occurrence of PI in the source program with 3.142857. Thus the following statement
area = PI * radius * radius;is replaced as
area = 3.142857 * radius * radius;
Similarly the following statement
circum = 2 * PI * radius;
is replaced as:
circum = 2 * 3.142857 * radius;
After replacing all macros with its substitution text, the expanded source program is given to the C compiler. However, you can also use more than one macro in the program.
Now let us see another program that uses two macros LENGTH and BREADTH.
/* Program - abc1.c */#include
#define LENGTH 15
#define BREADTH 20
main{
int area, peri;
area = LENGTH * BREADTH;peri = 2 * (LENGTH + BREADTH);
printf ("\nThe area of a rectangle is : %d", area);printf ("\nThe perimeter of a rectangle is : %d", peri);
}The output of the program is as….
The area of a rectangle is : 300The perimeter of a rectangle is : 70
Macros are very useful in making C code more readable and compact. For example, consider the following definitions:
#define and &&#define or ||
These definitions enables the programmer to write more readable statements, such as:
In these programs we have used integer constants in place of substitution text. We can also use some other parameters such as string literals in place of substitution text. Program-abc2.c using string literals as a substitution text:
#include #define FALSE printf("\nYou are a big liar.") char name1[20]; printf ("\nEnter your name : "); scanf ("%s",name1); printf ("Enter your name again : "); scanf ("%s",name2); if (!(strcmp(name2,name1))) TRUE; else FALSE; } Here are the sample outputs of this program…. First Run: Enter your name : Amita Enter your name again : Amita You are a true person. Second Run: Enter your name : Amita Enter your name again : Anita You are a big liar. When this program is subjected to the C preprocessor, the C preprocessor replaces the text whenever it encounters a macro TRUE and when it traces FALSE. If our substitution text can be continued onto the next line then we will simply place a backslash (\) before the new line character. Program-abc4.c clears this concept more. #include #define STATEMENT1 "You know when I was young, I make fool elders. \ \nNow when I am elder, my younger make me fool. \ \nSo remember - history repeats itself." #define STATEMENT2 "I am sorry." main (){ char ch ; printf("\nDo you want to know my thoughts : "); scanf ("%c",&ch); if ((ch == 'y' ) || ( ch == 'Y' )) printf (STATEMENT1); else printf (STATEMENT2); } Here are the sample outputs of this program…. First Run: Do you want to know my thoughts : y You know when I was young, I make fool elders Now when I am elder, my younger make me fool. So remember - history repeats itself. Second Run: Do you want to know my thoughts : n I am sorry. While using macros, one should remember that there must be at least one blank space between identifier used as macro name and substitution text. And it is optional to have a blank space between # and define. If the substitution text is empty then the C preprocessor removes every occurrence of identifier from the source file, but note that the identifier is still considered defined and yields the value 1 when tested with #if directive. The #if directive will be discussed later on. The macros, which have arguments such that they may look and act like function calls are called as function-like macros. But macro call is not same as function call because unlike function call, no control is transferred to a macro. When a macro call encounters into the source program, the preprocessor replaces the macro templates with its substitution text. The syntax of function–like macros is as: Here the list of parameters consists of one or more formal parameters that are separated by commas. Remember that each parameter in list of parameters must be unique. Each parameter can appear more than once in substitution text and the names can appear in any order. Program-abc5.c uses a simple function–like macro. #include #define SUM(x,y) ( x+y ) main(){ int a, b, c; printf("\nEnter any two integer numbers : "); scanf("%d %d", &a, &b); c = SUM(a,b); printf("The addition of two numbers : %d", c); } The output of this program is as…. Enter any two integer numbers : 10 20 The addition of two numbers : 30 In this program, the actual arguments a and b are passed to the macro SUM(). In macro these actual arguments are collected into formal parameters x and y respectively. So whenever this program is compiled, the preprocessor in invoked firstly and then that preprocessor expanded the statement SUM(a, b) into the statement (x+y). Thus the statement c = (a+b); Notice that the macro definition itself (the line containing the preprocessor directive #define) does not include a semicolon. When the macro is invoked later, it is followed by an explicitly specified semicolon as Another main point to note is that the arguments names used in a macro are local to the macro in which it is defined. Thus there is no conflict between a macro’s formal parameter names and identifiers used in the program itself. In the definition of SUM, it does not matter if x and y are variables used in the program because the parameters x and y are replaced by the preprocessor.Since we know that there is no standard function provided in C to calculate the square of any number. So let us see anther simple program of function–like macro that squares any given number. #include #define SQUARE(x) (x*x) main (){ long int a, b, c, d; printf ("\nEnter any integer number : "); scanf ("%ld", &a); b= SQUARE(a) ; printf ("\nThe square of a number %ld is %ld", a, b); c = SQUARE(b); printf ("\nThe square of a number %ld is %ld", b, c); d = SQUARE(c); printf ("\nThe square of a number %ld is %ld", c, d); } Here are sample outputs of this program…. First Run: Enter any integer number : 2 The square of a number 2 is 4 The square of a number 4 is 16 The square of a number 16 is 256 Second Run: Enter any integer number : 5 The square of a number 5 is 2 The square of a number 25 is 625 The square of a number 625 is 390625 No doubt good usage of #define will often reduce the need for comments within the program. Consider the following statement: printf(“\n%d is a leap year.”, year); else printf(“\n%d is not a leap year.”, year); Here the expression tests whether the variable year is a leap year or not. The above code segment can be made more easier by using #define directive as: and then using this we can rewrite the earlier if-statement as: printf(“\n%d is a leap year.”, year); else printf(“\n%d is not a leap year.”, year); Program-abc7.c illustrates this fact. #include #define IS_LEAP_YEAR(year) (year%4==0)&&(year%100!=0)||(year%400==0) main(){ int year; printf("\nEnter a year : "); scanf("%d", &year); if (IS_LEAP_YEAR(year)) printf("%d is a leap year.", year); else printf("%d is not a leap year.", year); } Here are some sample outputs of this program…. First Run: Enter a year : 1900 1900 is not a leap year. Second Run: Enter a year : 1784 1784 is a leap year. C preprocessor also provides the facility of using nested macros. By nested macros, we mean a macro call within another macro call. Program-abc8.c illustrates this concept of nested macro more. #include #define MAX(x,y) ((x>y) ? (x):(y)) main(){ int a, b, c, large; printf ("\nEnter any three integer numbers : "); scanf ("%d %d %d", &a, &b, &c); large = MAX(a, MAX(b,c)); printf("The largest of these three numbers is %d", large); } Here are sample outputs of this program…. First Run: Enter any three integer numbers : 10 15 20 The largest of these three numbers is 20 Second Run: Enter any three integer numbers : 10 15 12 The largest of these three numbers is 15 When we use macro with arguments, we must remember that the actual argument list must have same number of arguments as formal parameter list. The formal parameter list must be enclosed in parentheses and there should be no blank space between the macro name and opening parentheses of parameters list. That is, following macro definition is completely wrong: And if out substitution text is an expression then it is compulsory to enclosed this expression in parentheses otherwise you must be ready for wrong result. Watch the output of the Program-abc9.c, if you do not use parenthesis in substitution text. #include #define SUM(x,y) x+y main(){ int a = 20; int b = 20; int number; number = a * b / SUM(a, b); printf("%d", number); } What should be the output of this program you expect? You would tell 10. But certainly it is wrong. Because C preprocessor encounter the macro call SUM(20,20), it immediately replaces it with 20+20 and the statement number = 20 * 20 / 20 +20 and it results in 40. And if we enclose the substitution text in parenthesis as (x+y) then the statement number = 20 * 20 / (20+20) and and it would result in 10. Thus the importance of parentheses in macro definition can not be overstressed. We can also make a mixed call, that is, calling one macro into another different macro. Program-abc10.c calls object–like macro into function like macro. #include #define LENGTH 10 #define AREA(x) (x*x) main (){ printf("\nArea of a square : %d", AREA(LENGTH)); } The output of this program is as…. Area of a square : 100 The stringizing operator (#) is used only with macros that takes arguments. The formal parameter that preceded the stringing operator in substitution text then when we pass the actual argument to the macro then it is enclosed in double quotation marks and treated as string literal. Program-abc11.c illustrates this concept: #include #define String(str) printf(#str) main (){ printf("\n"); String(I am a naughty person.); printf("\n"); String("I am a naughty person."); } The output of this program is as…. I am a naughty person. I am a naughty person. The token pasting operator (##) is used in both object–like macros and function–like macros. This operator is used to join separate tokens into a single token. Therefore it can not be first and last token in the macro definition.When we use formal argument with token pasting operator (##), the formal argument is immediately substituted by the unexpanded actual argument. Program-abc12.c uses token pasting operator (##). #include #define example(n) printf("NUMBER" #n " = %d",number##n) main (){ int number7 = 10; example (7); } The output of this program is as…. NUMBER7 = 10 The #undef directive is used to undefine a preprocessor symbol that has already been defined. The syntax of #undef directive is: The #undef directive removes the current definition of identifier. Once an identifier is undefined, the preprocessor ignores the subsequent occurrences of identifier. The purpose of #undef is just reverse of the #define directive that has empty substitution text argument. The #undef is typically paired with #define directive to create a region in a source program in which an identifier has a special meaning. For example, suppose a program wishes the constant NULL to have a value –1, the program could begin with the following directives: #undef NULL #define NULL –1 But remind that it would be dangerous to redefine the value of NULL; it is just to show you an example.It is not compulsory to perform the redefinition at the beginning of the program. You can also redefine in the middle of a program so that it has one value in the first part and another value at the end. Another main point to note is that a symbol need not be redefined after it is undefined. Sometimes we need to accumulate a collection of commonly used constants and macro definitions that are used in almost every program. Therefore it is desirable to store these constants and definitions in a file that can be inserted automatically into every program. Such files are called as header files and stored either in standard directory or in a source directory. By convention, header file names end with the character ‘.h’.The preprocessor directive, #include, is used to include the contents of other files into the current source file. Usually, however, the files are included at the beginning of a program. The syntax of using the #include directive is as: #include Here the filename is the name of a file whose contents are added into the current source file and the file having that specific name should exist. Let we want to include the contents of a header file math.h in current source file, then there are two options of inserting it into the current source file: #include In the first declaration the preprocessor searches the file math.h into parent’s file directory firstly. For example, if you include a file named file2 within a file named file1, file1 is the parent file. If the file is not found then the processor searches the directories specified on the compiler command line and if the file is not still found then finally the standard libraries are searched. And after searching the file math.h its entire contents are inserted into the current file. In the second declaration, where the file specification is enclosed in angle brackets, the preprocessor does not search the current working directory. It begins its searching for the files in the directories specified on the compiler command line and then in the standard libraries. The compiler specifies the location of standard libraries. And after searching the file math.h, the preprocessor stops searching and insert its entire contents into the current file. If the file specification is enclosed in angle brackets, the preprocessor does not search the current directory. If you specify the complete path for the include file, between two sets of double quotation marks (“ ”) then the preprocessor searches only that specified path and ignore the standard directories. The C preprocessor also uses the facility of nesting of include files that is an #include directive can appear in a file named by another #include directive. When the include files are nested , the preprocessor starts its searching from the directories of parent file , then continues through the directories of any grandparent files. For example, if you include a file named file2 within a file named file1, file1 is the parent file. If file2 could include file3 then file1 would still the parent of file file2 but would be the “grandparent” of file1. One can use nesting of include files up to 10 levels. Once the nested #include is processed, the preprocessor continues to insert the enclosing include file into the original one. While using nested files, a file should not include itself because this would lead to infinite recursion. It can not even include another file that contains another file that includes the first file, either as this would also be an infinite recursion The C preprocessors provides some preprocessor directives that allow us to suppress compilation of some part of source program by testing a constant expression or identifier to determine which set of instructions would be passed to the compiler and which one would be suppressed from the source file. The C preprocessor provides following directives to control the flow of program : The #if directive is used together with the #else, #elif and #endif directives in order to control the compilation of portions of a source program. The syntax of using these directives is as: [Statement-block] [#elif restricted-constant-expression Statement-block] [#elif restricted-constant-expression Statement-block] …. [#else Statement-block] #endif] Note that any number of #elif directives can appear between the #if and #endif directives, but atmost one #else directive is permitted. The #else directive, if present, must be the last directive before #endif. The #if and #elif test an expression that may include only integer constants values. No variables or function calls are permitted, nor are floating-point, characters or string context. The preprocessor directive selects a single Statement-block by evaluating the restricted-constant-expression following #if and each #elif directive until it finds a true (non-zero) restricted-constant-expression. If all occurrences of restricted-constant-expression are false (zero), the preprocessor selects the Statement-block after the #else directive. And if there is no #else directive and all instances of restricted-constant-expressions results in false then no Statement-block is executed. Remind that any statement block not selected by the preprocessor directive is removed from the file during preprocessing. Thus these statement blocks are not compiled. If the preprocessor selects a Statement-block then it processes that Statement-block first and then passes it to the compiler. If Statement-block itself contains preprocessor directives, the preprocessor carries out those directives first. Program-abc13.c uses #if directive together with #else and #endif directives #include #define RESULT 60 main(){ #if RESULT >= 40 printf("\nCongratulations you are pass." ); #else printf("\nSorry you are fail."); #endif } The output of this program is as…. Congratulations you are pass. However if you change the substitution text to 30 in place of 60 as: then you would get the following output: Sorry you are fail. In the above program if we use the preprocessor selects the statement Congratulations you are pass. and by pass the compilation of statement which is defined in #else directive. On the other hand, if we use the preprocessor selects the statement which is defined in #else directive Sorry you are fail. and by pass the compilation of statement which is defined in #if directive. The C preprocessor allows us to use nested conditional directives, that is in one conditional directive can be used in another conditional directive. Each nested #else or #endif directive belongs to the closest preceding #if directive. Program-abc14.c illustrates this concept. #include #define RESULT 29 main(){ #if (RESULT >= 60) printf("First Division"); #else #if (RESULT >=50) printf("Second Division"); #else #if (RESULT >=40) printf("Third Division"); #else printf("Fail"); #endif #endif #endif } The output of this program is as…. Fail Here each #if directive must be matched by a closing #endif directive. In this program instead of using so many #else-#if directives we can also use #elif directives. And another advantage of using #elif is that we will use just a single #endif conditional directive. The above program is redefined as: #include #define RESULT 29 main(){ #if (RESULT >= 60) printf("First Division"); #elif (RESULT >=50) printf("Second Division"); #elif (RESULT >=40) printf("Third Division"); #else printf("Fail"); #endif } The #ifdef and #ifndef directives works in same manner as the #if directives but it checks the constant expression on defined identifier only. When the C preprocessor encounters an #ifdef directive, it checks to see whether the identifier is defined (in macro template) or not defined. If the identifier is defined, the condition results in true (non-zero); otherwise it results in false (zero). And #ifndef directive works in reverse fashion. In this if the identifier has not been defined, or you can say that its definition has been removed with #undef, then the condition results in true (non-zero); otherwise it results in false (zero). These directives are provided only for compatibility with previous versions of the language. The syntax of the #ifdef is as: [Statement-block] #endif If the identifier is defined then the Statement-block is processed; otherwise the preprocessor suppresses Statement-block. Program-abc16.c illustrates this concept.
#define TEA main() { #ifdef TEA printf("\nTea is good for health."); printf("\nWhen you feel tired, take a cup of tea."); printf("\nTea has become the necessity of almost every human."); printf("\nGo and take a cup of tea."); #endif } The output of this program is as…. Tea is good for health. When you feel tired, take a cup of tea. Tea has become the necessity of almost every human. Go and take a cup of tea. In this program we have used four statements in the block of #ifdef directive. If the preprocessor finds TEA to be defined then it would process the block; otherwise it skips this block. After sometimes if you want to use the same code but with two option that if an identifier is defined then process Statement-block1; otherwise process Statement-block2. Program-abc17.c illustrates this. #include #define TEA main(){ #ifdef TEA printf("\nTea is good for health."); printf("\nWhen you feel tired, take a cup of tea."); printf("\nTea has become the necessity of almost every human."); printf("\nGo and take a cup of tea."); #else printf("\nTea is never good for health."); printf("\nYou can certainly live without tea. ") printf("\nGo and throw this habit."); #endif } When this program is processed, the preprocessor looks for identifier TEA. If it is defined then first Statement-block is compiled; otherwise Statement-block set is compiled. Similarly we can use #ifndef directive. The above program can be rewritten as: #include main(){ #ifndef TEA printf("\nTea is good for health."); printf("\nWhen you feel tired, take a cup of tea."); printf("\nTea has become the necessity of almost every human."); printf("\nGo and take a cup of tea."); #else printf("\nTea is never good for health."); printf("\nYou can certainly live without tea. "); printf("\nGo and throw this habit."); #endif } When this program is processed, the preprocessor looks for identifier TEA. If it is not defined then first Statement-block is compiled; otherwise Statement-block is compiled. The #line directive is used to control the processing of line. The syntax of #line directive is as: The constant value can be any integer constant and the filename can be any combination of characters and must be enclosed in double quotation marks. However if you omit the filename then the previous name remains unchanged. The #line directive instructs the preprocessor to change the compiler’s internally stored line number and file name to a given line number and file name. We use this facility in debugging of errors during the compilation of our source program. The line number normally refers to the current line number and file name refers to the current file name. When we change the line number, the compiler ignores the previous value. For example In this, the internally stored line number is changed and set to 20 and the file name is also changed to example.c. ANSI C has already defined some macros, such as __LINE__ for obtaining the line number of the current source line and __FILE__ for obtaining the current source file. Additionally it provides __DATE__ and __TIME__ for obtaining the date of compilation in the form Mmmddyyyy and for obtaining the time of compilation in the form “hh:mm:ss” respectively. A pragma directive is used to instruct the compiler to a particular action at compile time without affecting the program as a whole. The meaning of pragmas may vary from one compiler to another. The syntax of pragma is as: The char-seq is a series of characters that gives a specific compiler instruction and argument, if any. An recognized pragma is ignored. For example #pragma warning -xyz #pragma warning .xyz This pragma is used to change the state of warning message. -xyz is used to turn – off the warning xyz .xyz is used to toggle the On/off State If there is no character or letter after the number sign, then it is said to be Null directive. A directive of the following form is a null directive. In this case the preprocessor takes no action except to eliminate the line./* Program - abc2.c */
printf("\nYou are a true person.")
printf("\nYou are a big liar.")
/* Program - abc4.c */
Function Like Macros
#define
/* Program - abc5.c */
c = SUM (a,b); is equivalent to
SUM(a, b);
/* Program - abc6.c */
if ((year % 4 == 0 && (year %100 != 0) || (year %400 == 0))
#define IS_LEAP_YEAR(year) (year % 4 == 0 && (year %100 != 0) || (year %400 == 0)
if (IS_LEAP_YEAR(year))
/* Program - abc7.c */
Nested Macros
/* Program - abc08.c */
#define MAX (x,y) ((x>y) ? (x):(y))
/* Program - abc9.c */
number = a * b / SUM (a, b); would be expanded as
number = a * b / SUM (a,b); would be expanded as
/* Program - abc10.c */
Macro Using Sringizing Operator (#)
/* Program - abc11.c */
Macro Using Token Pasting Operator (##)
/* Program - abc12.c */
The #undef Directive
#undef identifier
#include
File Inclusion : #include Directive
#include “filename”
#include “math.h”
Conditional Compilation
The #if, #else, #elif and #endif Directives
#if restricted-constant-expression
/* Program - abc13.c */
#define RESULT 30
#define RESULT 60
#define RESULT 30
/* Program - abc14.c */
/* Program - abc15.c */
The #ifdef and #ifndef Directives
#ifdef identifier
#include
/* Program - abc17.c */
/* Program - abc18.c */
Line Control
#line constant
#line 20 “example.c”
Pragmas
#pragma char_seq
#pragma warning +xyz
+xyz is used to turn – on the warning xyz
Null Directive
Conclusion
C Preprocessor is a program that takes our source program before it is transferred to the compiler. C preprocessor directives are broadly divided into four categories – macro substitution, file inclusion, conditional compilation and line control. These preprocessor directives are also called as preprocessor commands. The C compiler automatically invokes the preprocessor in its first pass compilation and it does not matter whether you are using any preprocessor directive or not.