Preprocessor Directives in C
Preprocessor Directives
The preprocessor is a translation phase that is applied to the source code before it is handed over to the compiler. The preprocessor commands can form a whole new language itself.
The preprocessor performs textual substitutions on the source code in three different ways –
- File Inclusion – taking the content of another file into the source file just as if we had typed it all in there.
- Macro Substitution – replacing instances of one piece of text with another.
- Conditional compilation – arranging the code in such a manner that depending on one more true/false conditions, certain parts of the source code are seen or not seen by the compiler at all.
Uniqueness of preprocessors
- Preprocessor directives starts with #.
- Preprocessor directives are line based. Every preprocessor needs to be written in new line.
- The preprocessor does not know the structure of C.
File Inclusion
The File inclusion preprocessor directive us used when contents of a file can be included in the another file to get advantage of readymade constants, variable and functions etc. The contents of a file filename.h (called header file) can be included in another file(source file) by using the line
#include<filename.h> or
#include “filename.h”
Both of these lines are valid preprocessor ,these above lines cause the contents of the file filename.h to be read, parsed and compiled at that point. After this file is processed compilation continues on the line just after the #include line.
What is the significance of the extension .h in the header files?
The extension .h simply stands for header , and reflects the fact that #include directives are usually placed at the head of our source files, and contain global declarations and definitions which you would otherwise put at the head of the source file. However, the extension .h is not mandatory and we can give any extension to our own header files. But .h is traditional, and recommended as it suits the short name for header.
- What is the advantage of using header files?
- Whatever functions and other declarations are put into a header file, they can be made available to several source files using #include.
- Instead of using a header file, if we have to type the same matter that is contained in the header file, in each of the source files directly, it would be too hectic. Further, if something ever changed in that matter, we’d have to edit all those source files. Still further, if we missed to modify some of these source files, then our program could fail due to the mismatched declarations (i.e. due to the incompatibility between the new declaration in one source file and the old one in a source file and definitions into a header file is advantageous in the sense that if there is a change in it, then it only have to be changed in one place, and that change will be reflected to all the source files that include that header file.
What should be put in a header file?
There are a few things not to be put in header files. They are :
- Defining instances of global variables. If we put the defining instance of global variables in a header file, and include that header file, and include that header file in more than one source file, the variable will end up multiply defined.
- Function bodies. These are also defining instances. We should not put the function bodies (or function definitions) in the header files for the same reason as was with the global variables. that is, it’s likely that we’ll end up with multiple copies of the function and hence “multiply defined” errors.
What happens when we #include a header file.
Header files typically contain only external declarations, and should not contain function bodies. The header file may provide the declaration for some functions, so that the compiler can
- generate correct code when we call them
- make sure that we’re calling the functions correctly (i.e. with correct number and types of arguments and with correct return type).
but the header file does not give the compiler the definitions of these functions. The actual definitions of these functions (which are in the form of a precompiled code in a separate library) will be combined into our program at the end of compilation, be the part of the compiler tell the compiler/linker where to find them. in particular, if we are using a third-party library containing some useful functions, the library will often come with a header file describing those functions. Using the library is therefore a two-step process:
- #include the header file in the source files where we want to call the library functions, and
- we must tell the linker to read in the functions from the library itself.
Novice programmers sometimes do the following
- They place commonly-used functions (with their definitions) in header files and then use #include to bring them (once) into each program where they use that function,
or
- They use #include to bring together the several source files making up a program but both of these approaches are not good. It’s much better to learn how to use the compiler or linker to combine together separately compiled object files.
Macro Definition and substitution
A preprocessor line of the form
#define name text
defines a macro with the give name, whose value is the given replacement text. After that (for the rest of the current source file), wherever the preprocessor encounters that name, it will replace it with the replacement text. The name follows the same rules as ordinary identifiers, that is
- It can contain only letters, digits, and underscores.
- It may not begin with a digit.
- It may begin with an underscore.
Since macros behave quite differently from normal variables (or function), the common programming practice is to give them names that are all capital letters (or at least first letter as a capital letter). The replacement text can be anything, and it is not limited to numeric constants or simple strings, or anything. Macros is commonly used to define various constants used in a program to make them more self-documenting.
#define MAX 50
# define TRUE 1
#define False 0
#define INF_ loop while(1)
See the following piece of code,
if (employee<50) … else if (employee<50) … else …
Now , if later on, there is a need to compare the age with 60, then all the occurrences of 50 scattered throughout the program, will have to be replaced by 60. But this is neither readable nor reliable. It’s not obvious from the above code that,
- in what context all those 50’s scattered around the program are used (whether they are age of the employee, or number of employees etc).
- if we ever decide that the employee should be compared with a value 60, instead of 50, then we’ll have to remember to change the number in two (or more) places. A much better solution is to use a macro:
#define AGE 50 … if(employee>AGE) …. else if(employee<AGE) … else
Conditional compilation
The preprocessor has various directives to compile the lines of code based on come condition(s).
This is known as conditional compilation. The C preprocessor provides a variety of directives to
perform conditional compilation. The first one is having the following format,
#if constant_expression
….
#endif
the code between #if and #endif compile if constant_ expression evaluates to TRUE (i.e., nonzero).
Otherwise, the preprocessor removes them the source file, and the compiler never sees them. For example:
#include<stdio.h> main() { #if(_ _ TURBOC_ _) printf(“We are using Turbo c.”); #endif #if(_ _BORLANDC _ _) print(“We are using Turbo C.”); #endif }
Output: We are using Turbo C.
It can also be written like this as well-
#if constant_expression … #else … #endif
The code that gets compiled depends on whether constant_expression is evaluated to zero or nonzero . If it nonzero, then the code between #if and #else gets compiled and the code between #else and #endif is ignored. If constant_ expression is evaluated to Zero, then the code between #else and #endif gets compiled and the code between #if and #else is ignored.
Example:
#include<stdio.h> main() { #if (_ _ TURBOC_ _) printf (“Turbo c compiler >”); #else print(“Any other compiler.”); #endif }
The another way can be –
#if constant-expression 1 section1 #elif constant- expression2 section2 … #elif constant-expression section #else final_section #endif
Here control moves to the matching #endif. If constant-expression2 is false, control passes to next #elif, and so on until either #else or #endif is encountered. in this format, if the constant – expressiona1 evaluates (according to the macro expansion) to nonzero (true), the lines of code in section1 will be preprocessed (the control passes to the matching #endif) and passed to compiler. If the constant-expression1 evaluates to zero (false), then section1 is ignored (I.e., no macro expansion and no compilation), and the control passes to the first #elif line where constant-expression2 is evaluated. If true, section2 is preprocessed, after which the c
#ifdef and #ifndef directives
the #ifdef and #ifndef conditional directives are used to test whether an identifier is currently defined or not. That is, whether a previous #define directive has been processed for that identifier, and is still in force or not. The line
#ifdef identifier
is same as
#if 1
if identifier is currently defined, and is same as
#if 0
if identifier is currently undefined.
The line
# ifndef identifier
is same as
#if 0
if identifier is currently defined, and is same as
#if 1
if identifier is currently undefined.
Both the # ifdef and the # ifndef directives can use # else directive, but not the # elif directive.
undefined operator
The macro defined using #defined can be made ineffective by using its reverse macro #undef. Its format is,
#undef identifier
For example,
#include<stdio.h> #define square (x) (x)*(x) #define cube(x) x*square(x) main() { printf(“%d”,cube(5)); #undef square /*undefined the macro square now */ #ifndef square /*confirm that square is no more */ printf(“\n This macro is no more defined”); #endif }
The defined operator
The defined operator provides an alternate and flexible way to test whether the combinations of identifiers are defined or not. This operator is can only be used with # if and #elf directives. The expression
defined (identifier ) or defined identifier
evaluates to 1 (true) if the identifier has been defined previously (using #define) and not yet undefined ((using #under). Otherwise, it evaluates to 0 (false). The directive
#if defined (identifier)
is same as,
#ifdef identifier
Predefined macros
The Turbo C standard has following predefined macro names:
–LINE– : this macro provides the number of the current source file line being processed. Usually, the first line of a source file is defined to be 1, though the # line directive can affect this.
–FILE– : this macro provides the number of the current source file being processed. This macro changes whenever the compiler processes an # include directive, or a # line directive, or when the # include file is complete.
–DATE– : third macro provides the date when the preprocessor began processing the current source file. The format in which date appears is : mm did icy. Here mmm is for month (Jan, Feb, …, Dec), dd for day (01,02, …,31), and yyy for year (1999,2000,2001,2002,etc). Each occurrence of the macro in a given file will give the same value, regardless of how long the processing takes.
–TIME– :this macro keeps track of the time the preprocessor processing the current source file. It takes the format hh:mm:ss, where hh is for hour (00to 23), mm for minutes (00 to 59), and ss for seconds (00 to 59).
–PASCAL– :this macro is defined to signal that the –P flag, or the Pascal calling convention has been used. This macro is set to integer constant 1 if used, otherwise it remains undefined.
–CDECL– :this macro is defined if standard C calling conventions are conventions are used (i.e., it signals that the -P flag, or the Pascal calling conventions are not used). It is set to 1 if the Pascal calling convention was not used, otherwise it is undefined.
–COMPACT–, –LARGE — , — MEDIUM — , — SMALL — , — TINY — , are the 6 macro names defined on the basis of the memory model chosen at compile-time. Only one of these macro names is defined for any given compilation, and the others, by definition, are undefined. For example, if we compile with the small memory model, the –SMALL—memory model is defined, and the rest are not. So, the directive
#if defined(__Small__)
Will be true, while
# if defined(__ LARGE__)
(or any of the others)will be false
#error directive
The # error directive has the following syntax :
# error error_message
this generates the error message :
Error: filename line# : Error directive: error message
#error directive is usually embedded in a preprocessor conditional that catches some undesired compile-time condition. In the normal case, that condition will be false. If the condition is
true, we want the compiler to print an error message and stop compiling. This we can do by embedding an #error directive within a conditional that is true for the undesired case.
In what situation is conditional compilation is advantageous
conditional compilation is advantageous mainly When we have to write a portable code
Suppose we want to write a code that will run on any machine, or any platform (UNIX,DOS, Windows, OS/2,etc), or any compiler. But the way we are trying to do something is different depending on what compiler, operating system. or computer we are using. In such situations, what we can do is that we can place different versions of our code, one for each situation, between suitable # ifdef directives, and when we compile the program in a particular environment, we arrange to have the macro names defined which select the section that we need in that environment.
The assert() Macro
A convenient debugging tool is defined in the header fie assert .h. this tool is a macro assert(), which checks an expression that we are “asserting as true” . The preprocessor generates code for testing the assertion. Assertion is a statement that we expect to be true at run time. If the assertion fails, an error message will be flagged by the system (telling what the Assertion was and also that this assertion failed) and the program will stop.
The syntax of
the assert macro is :
assert(expression);
if the expression evaluates to TRUE, nothing happens. But if the expression evaluates to FALSE, the program execution terminates immediately after issuing an error message. This error message contains the source file name, line number, and the expression that failed the assertion.
Some examples of assertions are :
assert (n>0);
assert(ch>=’a’ && ch<=’z’);
assert(num>=’0’ && nu<=’9’);
Following program will illustrates the use of assert() macro in the program:
Please Like and share the topic Preprocessor directives in C Language.
Similar Posts:
You may also like:
Multiple GST and Interstate Sales invoice Discounts in Sales Invoices in Tally.ERP9 Update GST for Stock Items and Groups
Data Types in JavaScript JavaScript Control Statements and Operators JavaScript Functions and Loops
HTML Introduction What is New in HTML5
Download Official TurboC IDE cum Compiler from here
Difference between if-else statement and directive # if .. # else.. #endif ?
The #if .. #else .. #endif looks somewhat like an if statement, but it behaves completely in a different manner. An if statement controls which statement of the program are to be executed at run time, but #if .. #else.. #endif controls which parts of the program actually get compile.
What is the difference between #include<stdio.h> and #include”stdio.h”
The difference between the two forms is in the way the preprocessor searches for stdio.h. The preprocessor searches for the files enclosed in < > in standard directories. It searches for the files enclosed in “” in the current directory or the directory containing the source file that is including stdio.h. Therefore “” is usually used for header files that you have written and <> is usually used for headers that are provided for you (written by someone else).
[…] Preprocessor Directives in C […]
[…] Topics : Recursion in C Preprocessor Directives in C File, Stream and Standard […]
[…] File Handling in C Language File, Stream and Standard I/O Preprocessor Directives in C […]