The next most useful preprocessor directives are ifdef and endif . These are always used in pairs with the basic syntax
#ifdef identifier . . . #endifAll the code between the #ifdef and the #endif is skipped (i.e. completely ignored) unless the identifier has been the subject of a #define directive or has been defined in some other way.
#ifdef may be written as
#if defined ... and #ifndef may be written as
#if !defined ... defined is a special unary operator only understood by the pre-processor. An obvious and useful example is shown below. This program is called debug .
#include <stdio.h>
#include <ctype.h>
#define DEBUG 1
main()
{
char inbuf[BUFSIZ];
int i = 0;
int lcnt = 0;
gets(inbuf);
while(*(inbuf+i))
{
#ifdef DEBUG
printf("Character %d Value %c(%o)\n",
i,*(inbuf+i),*(inbuf+i));
#endif
if(isalpha(*(inbuf+i)))lcnt++;
#ifdef DEBUG
printf("Letter Count %d\n",lcnt);
#endif
i++;
}
printf("Total letters %d\n",lcnt);
}
A typical dialogue is shown. $ debug the cat Character 0 Value t(164) Letter Count 1 Character 1 Value h(150) Letter Count 2 Character 2 Value e(145) Letter Count 3 Character 3 Value (40) Letter Count 3 Character 4 Value c(143) Letter Count 4 Character 5 Value a(141) Letter Count 5 Character 6 Value t(164) Letter Count 6 Total letters 6If the line defining DEBUG were removed or commented out then the diagnostic printf() function calls would not be seen by the compiler and the diagnostic printing would be omitted. This is a common practice.
There are two other ways in which an identifier associated with #define may become defined. The first method is from the compiler command line. A typical Unix system call would be
cc debug.c -DDEBUG this forces the compiler to see DEBUG as defined even though no actual value is associated with DEBUG . The usage
cc debug.c -DDEBUG=1 may be used to associate a particular value with DEBUG . The compiler command line definition of an identifier is a particularly useful thing to do because it means that debugging code can be switched on and off without making any alterations whatsoever to the source code.
The second method applies only to certain special symbols. There are certain symbols that are defined internally by the preprocessor. The ANSI standard symbols predefined in this way are
| symbol | meaning |
|---|---|
| __STDC__ | defined if the compiler is ANSI |
| __LINE__ | the current source line number |
| __FILE__ | the source file name |
| __DATE__ | the date when the program was compiled |
| __TIME__ | the time when the program was compiled |
In case your printer or display didn't make it clear these all consist of four letters preceded by and followed by a pair of underline characters. All except __STDC__ and __LINE__ are character strings complete with enclosing double quotes. The following might be found in a program
#ifdef __STDC__
double reciprocal(double x)
#else
double reciprocal(x)
double x;
#endif
{
return 1.0/x;
}
providing a function definition acceptable to both ANSI and pre-ANSI compilers. The logic associated with #ifdef may be extended using #else and #elif is a fairly obvious way. You may well encounter code such as
#ifdef UNIX #define INTBITS 32 #else #define INTBITS 16 #endifThis sort of thing is common in programs intended to be ported across a wide range of systems although some of the effects can be achieved more cleanly using limits.h if ANSI compilers are available.
To provide greater flexibility any #define d identifier can be undefined by the directive #undef followed by the identifier. The directive #line forces the compiler to have a particular idea of the current line number in the source. This is only useful in conjunction with __LINE__.
The directive #pragma allows special compiler specific information to be included in the program. The meanings of pragmas are always compiler specific, they may, for example, be used to specify a particular way of storing pointers or generating code.