C Preprocessor-I

As we had discussed Macro Expansion in previous blog so now we will discuss the other directives- File Inclusion, Conditional Compilation and Miscellaneous Directives:

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. 

Now the Question arises when and why this feature is used?

So the answer is
It can be used in two cases:
(a) 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.

(b) 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 <goto.c>            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.

Now the Question arises that...
Where would #ifdef be useful? When would you like to compile only a part of your program?

In three cases it can be useful:

(a) To “comment out” obsolete lines of code. It often happens that a program is changed at the last minute to satisfy a client. This involves rewriting some part of source code to the
client’s satisfaction and deleting the old code. But veteran programmers are familiar with the clients who change their mind and want the old code back again just the way it was.
Now you would definitely not like to retype the deleted code again.
One solution in such a situation is to put the old code within a pair of /* */ combination. But we might have already written a comment in the code that we are about to “comment out”. This would mean we end up with nested comments. Obviously, this solution won’t work since we can’t nest comments in C.

Therefore the solution is to use conditional compilation as shown below.

main( )
{
#ifdef OKAY
statement 1 ;
statement 2 ; /* detects virus */
statement 3 ;
statement 4 ; /* specific to stone virus */
#endif
statement 5 ;
statement 6 ;
statement 7 ;
}

Here, statements 1, 2, 3 and 4 would get compiled only if the macro OKAY has been defined, and we have purposefully omitted the definition of the macro OKAY. At a later date, if we want that these statements should also get compiled all that we are required to do is to delete the #ifdef and #endif statements.

(b) A more sophisticated use of #ifdef has to do with making the programs portable, i.e. to make them work on two totally different computers. Suppose an organization has two different types of computers and you are expected to write a program that works on both the machines. You can do so by isolating the lines of code that must be different for each machine by marking them off with #ifdef. For example:

main( )
{
#ifdef INTEL
code suitable for a Intel PC
#else
code suitable for a Motorola PC
#endif
code common to both the computers
}

When you compile this program it would compile only the code suitable for a Intel PC and the common code. This is because the macro INTEL has not been defined. 

Note that the working of #ifdef - #else - #endif is similar to the ordinary if - else control instruction of C.

If you want to run your program on a Motorola PC, just add a statement at the top saying,
#define INTEL
Sometimes, instead of #ifdef the #ifndef directive is used. The #ifndef (which means ‘if not defined’) works exactly opposite to #ifdef. The above example if written using #ifndef, would look like this:

main( )
{
#ifndef INTEL
code suitable for a Intel PC
#else
code suitable for a Motorola PC
#endif
code common to both the computers
}

(c) Suppose a function myfunc( ) is defined in a file ‘myfile.h’ which is #included in a file ‘myfile1.h’. Now in your program file if you #include both ‘myfile.h’ and ‘myfile1.h’ the compiler flashes an error ‘Multiple declaration for myfunc’. This is because the same file ‘myfile.h’ gets included twice. To avoid this we can write following code in the header file.

/* myfile.h */
#ifndef __myfile_h
#define __myfile_h
myfunc( )
{
/* some code */
}
#endif

First time the file ‘myfile.h’ gets included the preprocessor checks whether a macro called __myfile_h has been defined or not. If it has not been then it gets defined and the rest of the
code gets included. Next time we attempt to include the same file, the inclusion is prevented since __myfile_h already stands defined.

#if and #elif Directives
The #if directive can be used to test whether an expression evaluates to a nonzero value or not. If the result of the expression is nonzero, then subsequent lines upto a #else, #elif or #endif are compiled, otherwise they are skipped.
A simple example of #if directive is shown below:

main( )
{
#if TEST <= 5
statement 1 ;
statement 2 ;
statement 3 ;
#else
statement 4 ;
statement 5 ;
statement 6 ;
#endif
}

If the expression, TEST <= 5 evaluates to true then statements 1, 2 and 3 are compiled otherwise statements 4, 5 and 6 are compiled.

Miscellaneous Directives

Miscellaneous Directives
There are two more preprocessor directives available, though they are not very commonly used. They are:
(a) #undef
(b) #pragma

#undef Directive
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.

#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. Some of these pragmas are
discussed below.

(a) #pragma startup and #pragma exit: These directives allow us to specify functions that are called upon program startup (before main( )) or program exit (just before the program
terminates). Their usage is as follows:

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

(b) #pragma warn: This directive tells the compiler whether or not we want to suppress a specific warning. Usage of this pragma is shown below.

#pragma warn –rvl /* return value */
#pragma warn –par /* parameter not used */
#pragma warn –rch /* unreachable code */
int f1( )
{
int a = 5 ;
}
void f2 ( int x )
{
printf ( "\nInside f2" ) ;
}
int f3( )
{
int x = 6 ;
return x ;
x++ ;
}
void main( )
{
   f1( ) ;
   f2 ( 7 ) ;
   f3( ) ;
}

If you go through the program you can notice three problems immediately. These are:
(a) Though promised, f1( ) doesn’t return a value.
(b) The parameter x that is passed to f2( ) is not being used anywhere in f2( ).
(c) The control can never reach x++ in f3( ).

If we compile the program we should expect warnings indicating the above problems. However, this does not happen since we have suppressed the warnings using the #pragma
directives. If we replace the ‘–’ sign with a ‘+’ then these warnings would be flashed on compilation. Though it is a bad practice to suppress warnings, at times it becomes useful to
suppress them. For example, if you have written a huge program and are trying to compile it, then to begin with you are more interested in locating the errors, rather than the
warnings. At such times you may suppress the warnings. Once you have located all errors, then you may turn on the warnings and sort them out.

Comments

  1. Coding | Gyansetu's coding courses are exceptional! Their instructors break down complex concepts into easily digestible lessons. Whether you're a novice or experienced programmer, Gyansetu is the ideal platform to hone your coding skills and keep up with industry trends.

    For more info:- https://www.gyansetu.in/blogs/future-scope-of-python-in-india/

    ReplyDelete

Post a Comment