HP C/HP-UX Online Help


Return to the Main HP C Online Help page



Preprocessing Directives

Overview of the Preprocessor
Source File Inclusion
Macro Replacement
Predefined Macros
Conditional Compilation
Line Control
Pragma Directive
Error Directive
Trigraph Sequences

Preprocessing directives function as compiler control lines. They allow you to direct the compiler to perform certain actions on the source file.

Overview of the Preprocessor

A preprocessor is a text processing program that manipulates the text within your source file. You enter preprocessing directives into your source file to direct the preprocessor to perform certain actions on the source file. For example, the preprocessor can replace tokens in the text, insert the contents of other files inot the source file, or supress the compilation of part of the file by conditionally removing sections of the text. It also expands preprocessor macros and conditionally strips out comments.

Syntax

preprocessor-directive ::=
    include-directive
newline
    macro-directive
newline
    conditional-directive
newline
    line-directive
newline
    error-directive
newline
    pragma-directive
newline
Description

The preprocessing directives control the following general functions:

All preprocessing directives begin with a pound sign (#) as the first character in a line of a source file. White space may precede the # character in preprocessing directives. The # character is followed by any number of spaces and horizontal tab characters and the preprocessing directive. The directive is terminated by a new-line character. You can continue directives, as well as normal source lines, over several lines by ending lines that are to be continued with a backslash (\).

Comments in the source file that are not passed through the preprocessor are replaced with a single white-space character.

Examples

include-directive:    
#include <stdio.h>
 
macro-directive:     

#define MAC x+y
 
conditional-directive:
#ifdef MAC
 
line-directive:      

#line 5 "myfile"
 
pragma-directive:   

#pragma INTRINSIC func

Source File Inclusion

(#include)

You can include the contents of other files within the source file using the #include directive.

Syntax

include-directive ::=

#include <filename>

#include "filename"
#include <i>identifier

Description

In the third form above, identifier must be in the form of one of the first two choices after macro replacement.

The #include preprocessing directive causes the compiler to switch its input file so that source is taken from the file named in the include directive. Historically, include files are named:

  filename.h
If the file name is enclosed in double quotation marks, the compiler searches your current directory for the specified file. If the file name is enclosed in angle brackets, the "system" directory is searched to find the named file. Refer to Refer to Chapter 10, "HP C/HP-UX Implementation Topics," in the HP C/HP-UX Reference Manual for a detailed description of how the directory is searched.

Files that are included may contain #include directives themselves. The HP C compiler supports a nesting level of at least 35 #include files.

The arguments to the #include directive are subject to macro replacement before the directive processes them. Error messages produced by the HP C compiler usually supply the file name the error occurred in as well as the file relative line number of the error.

Examples

#include <stdio.h>
 

#include "myheader"
 

#ifdef MINE

#define filename "file1"

#else

#define filename "file2"

#endif

#include filename

Macro Replacement

(#define, #undef)

You can define text substitutions in your source file with C macro definitions.

Syntax

macro-directive ::=

#define identifier [replacement-list]


#define identifier ( [identifier-list] )
         [replacement-list]


#undef identifier
 
replacement-list ::=
    token
    replacement-list token

Description

A #define preprocessing directive of the form:
#define identifier
[replacement-list]
defines the identifier as a macro name that represents the replacement list. The macro name is then replaced by the list of tokens wherever it appears in the source file (except inside of a string or character constant, or comment). A macro definition remains in force until it is undefined through the use of the #undef directive or until the end of the translation unit.

Macros can be redefined without an intervening #undef directive. Any parameters used must agree in number and spelling, and the replacement lists must be identical. All white space is treated equally.

The replacement-list may be empty. If the token list is not provided, the macro name is replaced with no characters.

If the define takes the form


#define identifier ([identifier-list]) replacement-list
a macro with formal parameters is defined. The macro name is the identifier and the formal parameters are provided by the identifier-list which is enclosed in parentheses. The first parenthesis must immediately follow the identifier with no intervening white space. If there is a space between the identifier and the (, the macro is defined as if it were the first form and that the replacement list begins with the ( character.

The formal parameters to the macro are separated with commas. They may or may not appear in the replacement list. When the macro is invoked, the actual arguments are placed in a parentheses-enclosed list following the macro name. Comma tokens enclosed in additional matching pairs of parentheses do not separate arguments but are themselves components of arguments.

The actual arguments replace the formal parameters in the token string when the macro is invoked.

If a formal parameter in the macro definition directive"s token string follows a # operator, it is replaced by the corresponding argument from the macro invocation, preceded and followed by a double-quote character (") to create a string literal. This feature may be used to turn macro arguments into strings. This feature is often used with the fact that the compiler concatenates adjacent strings.

After all replacements have taken place during macro invocation, each instance of the special ## token is deleted and the tokens preceding and following the ## are concatenated into a single token. This is useful in forming unique variable names within macros.

The following example illustrates the use of the # operator for creating string literals out of arguments and concatenating tokens:


#define debug(s, t) printf("x" # s "= %d, x" # t " %s", x## s, x ## t)
Invoked as: debug(1, 2);

Results in:

printf("x" "1" "= %d, x" "2" "= %s", x1, x2);
which, after concatenation, results in:
printf("x1= %d, x2= %s", x1, x2);
Spaces around the # and ## are optional.

NOTE  The # and ## operators are only supported in ANSI mode.

The most common use of the macro replacement is in defining a constant. Rather than hard coding constants in a program, you can name the constants using macros then use the names in place of actual constants. By changing the definition of the macro, you can more easily change the program:

#define ARRAY_SIZE 1000
 
float x[ARRAY_SIZE];
In this example, the array x is dimensioned using the macro ARRAY_SIZE rather than the constant 1000. Note that expressions that may use the array can also use the macro instead of the actual constant:
  for (i=0; i<ARRAY_SIZE; ++i) f+=x[i];
Changing the dimension of x means only changing the macro for ARRAY_SIZE; the dimension will change and so will all the expressions that make use of the dimension.

Some other common macros used by C programmers include:

#define FALSE 0

#define TRUE 1
The following macro is more complex. It has two parameters and will produce an in-line expression which is equal to the maximum of its two parameters:
#define MAX(x,y) ((x) > (y) ? (x) : (y))
Parentheses surrounding each argument and the resulting expression insure that the precedences of the arguments and the result will not improperly interact with any other operators that might be used with the MAX macro.

Using a macro definition for MAX has some advantages over a function definition. First, it executes faster because the macro generates in-line code, avoiding the overhead of a function call. Second, the MAX macro accepts any argument types. A functional implementation of MAX would be restricted to the types defined for the function. Note further that because each argument to the MAX macro appears in the token string more than once, check to be sure that the actual arguments to the MAX macro do not have any "side effects." The following example

  MAX(a++, b);
might not work as expected because the argument a is incremented two times when a is the maximum.

The following statement

  i = MAX(a, b+2);
is expanded to:
  i = ((a) > (b+2) ? (a) : (b+2));
Examples
#define isodd(n)  ( ((n % 2) == 1) ? (TRUE) : (FALSE))
/* This macro tests a number and returns TRUE if the number is odd. It will */
/* return FALSE otherwise.                                                  */
 
#define eatspace() while( (c=getc(input)) == " " || c == "\n" || c == "\t" );
/* This macro skips white spaces.                                           */

Predefined Macros

In addition to __LINE__ and __FILE__ (see Line Control (#line)), ANSI C provides the __DATE__, __TIME__ and __STDC__ predefined macros. Table 32: Predefined Macros describes the complete set of macros that are predefined to produce special information. They may not be undefined.
 

Table 32: Predefined Macros 
Macro Name  Description 
__DATE__ Produces the date of compilation in the form Mmm dd yyyy.
__FILE__ Produces the name of the file being compiled.
__LINE__ Produces the current source line number.
__STDC__ Produces the decimal constant 1, indicating that the implementation is standard-conforming.
__TIME__ Produces the time of compilation in the form hh:mm:ss.

NOTE  __DATE__, __TIME__, and __STDC__ are only defined in ANSI mode.

Conditional Compilation

(#if, #ifdef, ..#endif)

Conditional compilation directives allow you to delimit portions of code that are compiled if a condition is true.

Syntax

conditional-directive ::=
#if'     constant-expression newline [group]

#ifdef   identifier newline [group]

#ifndef identifier newline [group]

#else   newline [group]


#elif   constant-expression newline [group]

#endif
Here, constant-expression may also contain the defined operator:
defined identifier
defined (identifier)

Description

You can use #if, #ifdef, or #ifndef to mark the beginning of the block of code that will only be compiled conditionally. An #else directive optionally sets aside an alternative group of statements. You mark the end of the block using an #endif directive. The structure of the conditional compilation directives can be shown using the #if directive:
#if constant-expression
 .
 .
 .
/* (Code that compiles if the expression evaluates
    to a nonzero value.) */
#else
 .
 .
 .
 .
/* (Code that compiles if the expression evaluates
    to a zero value.) */

#endif
The constant-expression is like other C integral constant expressions except that all arithmetic is carried out in long int precision. Also, the expressions cannot use the sizeof operator, a cast, or an enumeration constant.

You can use the defined operator in the #if directive to use expressions that evaluate to 0 or 1 within a preprocessor line. This saves you from using nested preprocessing directives.

The parentheses around the identifier are optional. For example:

#if defined (MAX) && ! defined (MIN)
   .
   .
   .
Without using the defined operator, you would have to include the following two directives to perform the above example:

#ifdef max
#ifndef min
The #if preprocessing directive has the form:

#if constant-expression
Use #if to test an expression. The compiler evaluates the expression in the directive. If it is true (a nonzero value), the code following the directive is included. If the expression evaluates to false (a zero value), the compiler ignores the code up to the next #else, #endif, or #elif directive.

All macro identifiers that appear in the constant-expression are replaced by their current replacement lists before the expression is evaluated. All defined expressions are replaced with either 1 or 0 depending on their operands.

Whichever directive you use to begin the condition (#if, #ifdef, or #ifndef), you must use #endif to end the if-section.

The following preprocessing directives are used to test for a definition:

#ifdef identifier

#ifndef identifier
They behave like the #if directive but #ifdef is considered true if the identifier was previously defined using a #define directive or the -D option. #ifndef is considered true if the identifier is not yet defined.

You can nest these constructions. Delimit portions of the source program using conditional directives at the same level of nesting, or with a -D option on the command line.

Use the #else directive to specify an alternative section of code to be compiled if the #if, #ifdef, or #ifndef conditions fail. The code after the #else directive is compiled if the code following any of the if directives does not compile.

The #elifconstant-expression directive tests whether a condition of the previous #if, #ifdef, or #ifndef was false. #elif is syntactically the same as the #if directive and can be used in place of an #else directive.

Examples

Valid combinations of these conditional compilation directives follow:
#ifdef SWITCH                     /* compiled if SWITCH is defined */

#else                             /* compiled if SWITCH is undefined */

#endif                            /* end of if */

#if defined(THING)                /* compiled if THING is defined */

#endif                            /* end of if */

#if A>47                          /* compiled if A evaluates > 47 */

#else

#if A<20                          /* compiled if A evaluates < 20 */

#else                             /* compiled if A >= 20 and <= 47 */

#endif                            /* end of if, A < 20 */

#endif                            /* end of if, A > 47 */
Examples
#ifdef (HP9000_S800)                        /* If HP9000_S800 is defined, INT_SIZE */
#define INT_SIZE 32                         /* is defined to be 32 (bits) */

#elif defined (HPVECTRA) && defined (SMALL_MODEL)

#define INT_SIZE 16                         /* Otherwise, if HPVECTRA and */
#endif                                      /* SMALL_MODEL are defined,INT_SIZE is */
#ifdef DEBUG                                /* If DEBUG is defined, display the  */
       printf("table element : \n");        /* table elements.*/
       for (i=0; i < MAX_TABLE_SIZE; ++i)
           printf("%d  %f\n", i, table[i]);

#endif

Line Control

(#line)

You can cause the compiler to increment line numbers during compilation from a number specified in a line control directive. (The resulting line numbers appear in error message references, but do not alter the line numbers of the actual source code.)

Syntax

line-directive ::=
#line digit-sequence
[filename]

Description

The #line preprocessing directive causes the compiler to treat lines following it in the program as if the name of the source file were filename and the current line number is digit-sequence. This is to control the file name and line number that is given in diagnostic messages, for example. This feature is used primarily for preprocessor programs that generate C code. It enables them to force the HP C compiler to produce diagnostic messages with respect to the source code that is input to the preprocessor rather than the C source code that is output and subsequently input to the compiler.

HP C defines two macros that you can use for error diagnostics. The first is __LINE__, an integer constant equal to the value of the current line number. The second is __FILE__, a quoted string literal equal to the name of the input source file. Note that you can change

__FILE__ and __LINE__ using #include or #line directives.

Example

#line digit-sequence
[filename]:

#line 5 "myfile"

Pragma Directive

(#pragma)

You can provide instructions to the compiler through inclusion of pragmas.

Syntax

pragma-directive ::=

#pragma replacement-list

Description

The #pragma preprocessing directive provides implementation-dependent information to the compiler. See Compiling and Running HP C Programs for descriptions of pragmas recognized by HP C/HP-UX. Any pragma that is not recognized by the compiler is ignored.

Example

#pragma replacement-list:

#pragma intrinsic func

Error Directive

(#error)

Syntax

#error [pp-tokens]
The #error directive causes a diagnostic message, along with any included token arguments, to be produced by the compiler.

Examples

#ifndef (HP_C)

#error "HP_C not defined!"            /* This directive will produce
                                      /* the diagnostic message "HP_C
                                      /* not defined!"*/


#endif

#if TABLE_SIZE % 256 != 0
#error "TABLE_SIZE must be a multiple of 256!"

#endif                                /* This directive will produce
                                      /* the diagnostic message
                                      /* "TABLE_SIZE must be a
                                      /* multiple of 256! */

NOTE  The #error directive is only supported in ANSI mode.

Trigraph Sequences

The C source code character set is a superset of the ISO 646-1983 Invariant Code Set. To enable programs to be represented in the reduced set, trigraph sequences are defined to represent those characters not in the reduced set. A trigraph is a three character sequence that is replaced by a corresponding single character. Table 33 gives the complete list of trigraph sequences and their replacement characters.
 
Table 33: Trigraph Sequences and Replacement Characters 
Trigraph Sequence  Replacement 
??=
??/ \
??" ^
??( [
??) ]
??! |
??< {
??> }
??- ~

Any ? that does not begin one of the trigraphs listed above is not changed.