C preprocessor is exactly what its name implies. It is a program that processes our source program before it is passed to the compiler. Preprocessor commands (often known as directives) form what can almost be considered a language within C language. We can certainly write C programs without knowing anything about the preprocessor or its facilities
Features of C Preprocessor
The preprocessor offers several features called preprocessor directives. Each of these preprocessor directives begin with a # symbol. The directives can be placed anywhere in a program but are most often placed at the beginning of a program
preprocessor directives
- Macro expansion
- File inclusion
- Conditional Compilation
- Miscellaneous directives
Processor | Input | Output |
---|---|---|
Editor | Program typed from keyboard | C source code containing program and preprocessor commands |
Preprocessor | C source code file | Source code file with the preprocessing commands properly sorted out |
Compiler | Source code file with preprocessing commands sorted out | Relocatable object code |
Linker | Relocatable object code and the standard C library functions | Executable code in machine language |
Macro Expansion
#define UPPER 25
main( )
{
int i ;
for ( i = 1 ; i <= UPPER ; i++ )
printf ( "\n%d", i ) ;
}
In this program instead of writing 25 in the for loop we are writing it in the form of UPPER, which has already been defined before main( ) through the statement
define UPPER 25
This statement is called ‘macro definition’ or more commonly, just a ‘macro’. What purpose does it serve ? During preprocessing, the preprocessor replaces every occurrence of UPPER in the program with 25
Macros with Arguments
The macros that we have used so far are called simple macros. Macros can have arguments, just as functions can.
#define AREA(x) ( 3.14 * x * x )
main( )
{
float r1 = 6.25, r2 = 2.5, a ;
a = AREA ( r1 ) ;
printf ( "\nArea of circle = %f", a ) ;
a = AREA ( r2 ) ;
printf ( "\nArea of circle = %f", a ) ;
}
Here’s the output of the program…
Area of circle = 122.656250
Area of circle = 19.625000
Macros versus Functions
In a macro call the preprocessor replaces the macro template with its macro expansion, in a stupid, unthinking, literal way. As against this, in a function call the control is passed to a function along with certain arguments, some calculations are performed in the function and a useful value is returned back from the function
File Inclusion
This directive causes one file to be included in another. The preprocessor command for file inclusion looks like this:
include “filename”
and it simply causes the entire contents of filename to be inserted into the source code at that point in the program. Of course this presumes that the file being included is existing. When and why this feature is used? It can be used in two cases:
- If we have a very large program, the code is best divided into several different files, each containing a set of related functions. It is a good programming practice to keep different sections of a large program separate. These files are #included at the beginning of main program file
- There are some functions and some macro definitions that we need almost in all programs that we write. These commonly needed functions and macro definitions can be stored in a file, and that file can be included in every program we write, which would add all the statements in this file to our program as if we have typed them in
It is common for the files that are to be included to have a .h extension. This extension stands for ‘header file’, possibly because it contains statements which when included go to the head of your program. The prototypes of all the library functions are grouped into different categories and then stored in different header files. For example prototypes of all mathematics related functions are stored in the header file ‘math.h’, prototypes of console input/output functions are stored in the header file ‘conio.h’, and so on.
Actually there exist two ways to write #include statement. These are:
#include "filename"
#include <filename>
The meaning of each of these forms is given below:
include “goto.c” | This command would look for the file goto.c in the current directory as well as the specified list of directories as mentioned in the include search path that might have been set up |
#include | This command would look for the file goto.c in the specified list of directories only |
The include search path is nothing but a list of directories that would be searched for the file being included. Different C compilers let you set the search path in different manners. If you are using Turbo C/C++ compiler then the search path can be set up by selecting ‘Directories’ from the ‘Options’ menu. On doing this a dialog box appears. In this dialog box against ‘Include Directories’ we can specify the search path. We can also specify multiple include paths separated by ‘;’ (semicolon) as shown below:
c:\tc\lib ; c:\mylib ; d:\libfiles
The path can contain maximum of 127 characters. Both relative and absolute paths are valid. For example ‘..\dir\incfiles’ is a valid path
Conditional Compilation
We can, if we want, have the compiler skip over part of a source code by inserting the preprocessing commands #ifdef and #endif, which have the general form:
#ifdef macroname
statement 1 ;
statement 2 ;
statement 3 ;
#endif
If macroname has been #defined, the block of code will be processed as usual; otherwise not.
Example
main( )
{
#ifdef INTEL
code suitable for a Intel PC
#else
code suitable for a Motorola PC
#endif
code common to both the computers
}
Miscellaneous Directives
There are two more preprocessor directives available, though they are not very commonly used. They are
- #undef
- #pragma
On some occasions it may be desirable to cause a defined name to become ‘undefined’. This can be accomplished by means of the #undef directive. In order to undefine a macro that has been earlier #defined, the directive
#undef macro template
can be used. Thus the statement,
#undef PENTIUM
would cause the definition of PENTIUM to be removed from the system
#pragma Directive
This directive is another special-purpose directive that you can use to turn on or off certain features. Pragmas vary from one compiler to another. There are certain pragmas available with Microsoft C compiler that deal with formatting source listings and placing comments in the object file generated by the compiler. Turbo C/C++ compiler has got a pragma that allows you to suppress warnings generated by the compiler
Example
void fun1( ) ;
void fun2( ) ;
#pragma startup fun1
#pragma exit fun2
main( )
{
printf ( "\nInside maim" ) ;
}
void fun1( )
{
printf ( "\nInside fun1" ) ;
}
void fun2( )
{
printf ( "\nInside fun2" ) ;
}
And here is the output of the program.
Inside fun1
Inside main
Inside fun2
Note that the functions fun1( ) and fun2( ) should neither receive nor return any value. If we want two functions to get executed at startup then their pragmas should be defined in the reverse order in which you want to get them called.