Other Directives

I l @ ve RuBoard

Other Directives

The #undef directive cancels an earlier #define definition. The #if , #ifdef , # ifndef , #else , #elif , and #endif directives are typically used to produce files that can be compiled in more than one way.

The #undef Directive

The #undef directive undefines a given #define . That is, suppose you have this definition:

 #define LIMIT 400 

Then the directive

 #undef LIMIT 

removes that definition. Now, if you like, you can redefine LIMIT so that it has a new value. Even if LIMIT is not defined in the first place, it is still valid to undefine it. If you want to use a particular name and you are unsure whether it has been used previously, you can undefine it to be on the safe side.

With some compilers, you can redefine a defined name and nest #define and #undef directives so that undefining a name causes it to revert to its original value. This is not, however, the standard practice, and it is not part of the ANSI standard. More typically, you'll get a warning or error message if you redefine a name, so use #undef first if you want to redefine. Remember, however, that you can nest const constants as long as each new definition is in its own subblock, as Listing 16.5 shows.

Listing 16.5 The nestcnst.c program.
 /* nestcnst.c -- use const for nested constants */ #include <stdio.h> const int BIG = 20; void big(void); int main(void) {     const int BIG = 3;        /* hides global BIG            */     {         const int BIG = 100;  /* hides the BIG that equals 3 */         printf("%d\n", BIG);  /* displays 100                */     }     printf("%d\n", BIG);      /* displays 3                  */     big();     return 0; } void big(void) {     printf("%d\n", BIG);      /* displays 20                 */ } 

Conditional Compilation

You can use the other directives mentioned to set up conditional compilations. That is, you can use them to tell the compiler to accept or ignore blocks of information or code according to conditions at the time of compilation.

The #ifdef , #else , and #endif Directives

A short example will clarify what conditional compilation does. Consider the following:

 #ifdef MAVIS      #include "horse.h"  /* gets done if MAVIS is #defined    */      #define  STABLES     5 #else      #include "cow.h"    /* gets done if MAVIS isn't #defined */      #define  STABLES   15 #endif 

Here we've used the indentation allowed by newer implementations and by the ANSI standard. If you have an older implementation, you might have to move all the directives, or at least the # symbols (see the next example), to flush left:

 #ifdef MAVIS #   include "horse.h"  /* gets done if MAVIS is #defined    */ #   define  STABLES     5 #else #   include "cow.h"    /* gets done if MAVIS isn't #defined */ #   define  STABLES   15 #endif 

The #ifdef directive says that if the following identifier ( MAVIS ) has been defined by the preprocessor, then follow all the directives and compile all the C code up to the next #else or #endif , whichever comes first. If there is an #else , then everything from the #else to the #endif is done if the identifier isn't defined.

Incidentally, an "empty" definition like

 #define MAVIS 

is sufficient to define MAVIS for the purposes of #ifdef .

The form #ifdef #else is much like that of the C if else . The main difference is that the preprocessor doesn't recognize the braces ( {} ) method of marking a block, so it uses the #else (if any) and the #endif (which must be present) to mark blocks of directives. These conditional structures can be nested. You can use these directives to mark blocks of C statements, too, as Listing 16.6 illustrates.

Listing 16.6 The ifdef.c program.
 /* ifdef.c -- uses conditional compilation */ #include <stdio.h> #define JUST_CHECKING #define LIMIT 4 int main(void) {      int i;      int total = 0;      for (i = 1; i <= LIMIT; i++)      {           total += 2*i*i + 1; #ifdef JUST_CHECKING           printf("i=%d, running total = %d\n", i, total); #endif      }      printf("Grand total = %d\n", total);      return 0; } 

Compiling and running the program as shown produces this output:

 i=1, running total = 3 i=2, running total = 12 i=3, running total = 31 i=4, running total = 64 Grand total = 64 

If you omit the JUST_CHECKING definition (or enclose it inside a C comment) and recompile the program, only the final line is displayed. You can use this approach, for instance, to help in program debugging. Define JUST_CHECKING and use a judicious selection of #ifdefs , and the compiler will include program code for printing intermediate values for debugging. After everything is working, you can remove the definition and recompile. If, later, you find that you need the information again, you can reinsert the definition and avoid having to retype all the extra print statements. Another possibility is using #ifdef to select among alternative chunks of codes suited for different C implementations.

The #ifndef Directive

The #ifndef directive can be used with #else and #endif in the same way that #ifdef is. The #ifndef asks if the following identifier is not defined; #ifndef is the negative of #ifdef . This directive is often used to define a constant if it is not already defined.

 #ifndef SIZE    #define SIZE 100 #endif 

Again, older implementations might not permit indenting the #define directive. Suppose this directive were in an include file called arrays.h . Placing the line

 #include "arrays.h" 

at the head of a file would result in SIZE being defined as 100, but placing

 #define SIZE 10 #include "arrays.h" 

at the head would set SIZE to 10. Here, SIZE is defined by the time the lines in arrays.h are processed , so the #define SIZE 100 line is skipped . You might do this, for example, to test a program using a smaller array size. When it works to your satisfaction, you can remove the #define SIZE 10 statement and recompile. That way, you never have to worry about modifying the header array itself.

The #ifndef directive is commonly used to prevent multiple inclusions of a file. That is, a header file can be set up along the following lines:

 /* things.h */ #ifndef _THINGS__H_      #define _THINGS_H_      /* rest of include file */ #endif 

Suppose this file somehow got included several times. The first time the preprocessor encounters this include file, _THINGS_H_ is undefined, so the program proceeds to define _THINGS_H_ and to process the rest of the file. The next time the preprocessor encounters this file, _THINGS_H_ is defined, so the preprocessor skips the rest of the file.

Why would you include a file more than once? The most common reason is that many include files include other files, so you may include a file explicitly that another include file has already included. Why is this a problem? Some items that appear in include files, such as declarations of structure types, can appear only once in a file. The standard C header files use the #ifndef technique to avoid multiple inclusions. One problem is to make sure the identifier you are testing hasn't been defined elsewhere. The usual solution is to use the filename as the identifier, using uppercase, replacing periods with an underscore, and using an underscore (or, perhaps, two underscores) as a prefix and a suffix. Listing 16.7 shows an example

Listing 16.7 The names .h header file.
 /* names.h -- define names structure */ #ifndef _NAMES_H_ #define _NAMES_H_ #define SLEN 32 struct names {     char first[SLEN];     char last[SLEN]; }; #endif 

You can test this header file with the program shown in Listing 16.8 . This program should work correctly when using the header file shown in Listing 16.7 , and it should fail to compile if you remove the #ifndef protection from Listing 16.7 .

Listing 16.8 The doubincl.c program.
 /* doubincl.c -- include header twice */ #include <stdio.h> #include "names.h" #include "names.h"   /* accidental second inclusion */ int main() {     struct names winner = {"Less", "Ismoor"};     printf("The winner is %s %s.\n", winner.first,             winner.last);     return 0; } 
The #if and #elif Directives

The #if directive is more like the regular C if . It is followed by a constant integer expression that is considered true if non-zero , and you can use C's relational and logical operators with it.

 #if SYS == 1 #include "ibm.h" #endif 

You can use the #elif (not available in some older implementations) directive to extend an if-else sequence. For example, you could do this:

 #if SYS == 1      #include "ibmpc.h" #elif SYS == 2      #include "vax.h" #elif SYS == 3      #include "mac.h" #else      #include "general.h" #endif 

Many newer implementations offer a second way to test whether a name is defined. Instead of using

 #ifdef VAX 

you can use this form:

 #if defined (VAX) 

Here, defined is a preprocessor operator that returns 1 if its argument is #defined and otherwise . The advantage of this newer form is that it can be used with #elif . Using it, you can rewrite the previous example this way:

 #if defined (IBMPC)      #include "ibmpc.h" #elif defined (VAX)      #include "vax.h" #elif defined (MAC)      #include "mac.h" #else      #include "general.h" #endif 

If you were using these lines on, say, a VAX, you would have defined VAX somewhere earlier in the file with this line:

 #define VAX 

One use for these conditional compilation features is to make a program more portable. By changing a few key definitions at the beginning of a file, you can set up different values and include different files for different systems.

The ANSI C standard stipulates that implementations conforming to the standard predefine the name __STDC__ . Therefore, you can use the test

 #if defined (__STDC__) 

to check whether the compiler is ANSI C compliant.

I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net