HP OpenVMS Systems

HP CLanguage Reference Manual

Chapter 8Preprocessor Directives and Predefined Macros

The C preprocessor provides the ability to perform macro substitution,
conditional compilation, and inclusion of named files. Preprocessor
directives, lines beginning with
#
and possibly preceded by white space, are used to communicate with the
preprocessor.

The following sections describe the preprocessor directives and
operators available with the HP C compiler:

The
#define
and
#undef
directives, and the
#
and
##
operators ( Section 8.1)

Preprocessor directives are independent of the usual scope rules; they
remain in effect from their occurrence until the end of the compilation
unit or until their effect is canceled.

See Section 8.2 for more information about conditional compilation.
See your platform-specific HP C documentation for
implementation-defined information about preprocessor directives.

The ANSI standard allows only comments as text following a
preprocessing directive. The HP C compiler issues a warning if
this syntax rule is violated in all modes but the strict ANSI mode, in
which it issues an error message.

8.1 Macro Definition (#define and #undef)

The
#define
directive specifies a macro identifier and a replacement list, and
terminates with a new-line character. The replacement list, a sequence
of preprocessing tokens, is substituted for every subsequent occurrence
of that macro identifier in the program text, unless the identifier
occurs inside a character constant, a comment, or a literal string. The
#undef
directive is used to cancel a definition for a macro.

A macro definition is independent of block structure, and is in effect
from the
#define
directive that defines it until either a corresponding
#undef
directive or the end of the compilation unit is encountered.

The
#define
directive has the following syntax:

#define identifier replacement-list newline

#define identifier ( identifier-listopt )
replacement-listnewline

#define identifier ( ... ) replacement-listnewline

#define identifier ( identifier-list, ...replacement-listnewline

If the replacement-list is empty, subsequent occurrences of
the identifier are deleted from the source file.

The first form of the
#define
directive is called an object-like macro. The last three forms
are called function-like macros.

The
#undef
directive has the following syntax:

#undef identifier newline

This directive cancels a previous definition of the identifier by
#define
. Redefining a macro previously defined is not legal, unless the new
definition is precisely the same as the old.

The replacement list in the macro definition, as well as arguments in a
function-like macro reference, can contain other macro references.
HP C does not limit the depth to which such references can be
nested.

For a given macro definition, any macro names contained in the
replacement list are themselves replaced by their currently specified
replacement lists. If a macro name being defined is contained in its
own replacement list or in a nested replacement list, it is not
replaced. These nonreplaced macro names are then no longer available
for further replacement, even if they are later examined in contexts in
which they would otherwise be replaced.

On the first pass, the compiler replaces the identifier
AUTHOR
with the replacement list
james + LAST
. On the second pass, the compiler replaces the identifier
LAST
with its currently defined replacement list value. At line 9, the
replacement list value for
LAST
is the identifier
michener
, so
michener
is substituted at line 10. At line 12, the replacement list value for
LAST
is redefined to be the identifier
joyce
, so
joyce
is substituted at line 13.

The
#define
directive may be continued onto subsequent lines if necessary. To do
this, end each line to be continued with a backslash (\) immediately
followed by a new-line character. The backslash and new-line characters
do not become part of the definition. The first character in the next
line is logically adjacent to the character that immediately precedes
the backslash. The backslash/newline as a continuation sequence is
valid anywhere. However, comments within the definition line can be
continued without the backslash/newline.

If you plan to port programs to and from other C implementations, take
care in choosing which macro definitions to use within your programs,
because some implementations define different macros than others.

8.1.1 Object-Like Form

A preprocessing directive of the following form defines an object-like
macro that causes each subsequent occurrence of the macro name to be
replaced by the replacement list:

#define identifier replacement-list newline

An object like macro may be redefined by another
#define
directive provided that the second definition is an object-like macro
definition and the two replacement lists are identical. This means that
two files, each with a definition of a certain macro, must be
consistent in that definition.

The object-like form of macro definition defines a descriptive name for
a frequently used token. A common use of the directive is to define the
end-of-file (
EOF
) indicator as follows:

#define EOF (-1)

8.1.2 Function-Like Form

The function-like form of macro definition includes a list of
parameters. References to such macros look like function calls. When a
function is called, control passes from the program to the function at
run time; when a macro is referenced, source code is inserted into the
program at compile time. The parameters are replaced by the
corresponding arguments, and the text is inserted into the program
stream.

If there is a ... in the identifier-list in the macro
definition, then the trailing arguments, including any separating comma
preprocessing tokens, are merged to form a single item: the variable
arguments. The number of arguments so combined is such that, following
the merger, the number of arguments is one more than the number of
parameters in the macro definition (excluding the ...).

An identifier __VA_ARGS__ that occurs in the replacement-list
of
a function-like macro that uses ellipsis notation in the arguments is
treated as if it were a parameter, and the variable arguments form the
preprocessing tokens used to replace it.

If the replacement list is omitted from the macro definition, the
entire macro reference disappears from the source text.

The library macro
_toupper
, available on some systems in the
ctype.h
header file, is a good example of macro replacement. This macro is
defined as follows:

#define _toupper(c) ((c) >= 'a' && (c) <= 'z' ? (c) & 0X5F : (c))

When the macro
_toupper
is referenced, the compiler replaces the macro and its parameter with
the replacement list from the directive, substituting the argument of
the macro reference for each occurrence of the parameter (
c
in this case) in the replacement list.

The replacement list of C source code can be translated in the
following manner: if parameter
c
is a lowercase letter (between
'a'
and
'z'
), the expression evaluates to an uppercase letter (
c & 0X5F
); otherwise, it evaluates to the character as specified. This
replacement list uses the if-then-else conditional operator (
?:
). For more information about the conditional operator, see
Section 6.6. For more information about the bitwise operators, see
Section 6.5.6.

8.1.2.1 Rules for Specifying Macro Definitions

Preprocessor directives and macro references have syntax that is
independent of the C language. Follow these rules when specifying macro
definitions:

The macro name and the formal parameters are identifiers and are
specified according to the rules for identifiers in the C language.

Spaces, tabs, and comments may be used freely within a
#define
directive anywhere that the delta symbol ( Δ
) appears in the following example:

Δ
#Δ
define Δ
name(Δ
parm1Δ
,Δ
parm2Δ
)Δ
\ Δ
token-stringΔ

Spaces, tabs, and comments are replaced by a single space.

White space cannot appear between the name and the left parenthesis
that introduces the parameter list. White space may appear inside the
replacement list. Also, at least one space, tab, or comment must
separate name from
define
.

The identifier __VA_ARGS__ can occur only in the
replacement-list of a function-like macro that uses the
ellipsis notation in the arguments.

8.1.2.2 Rules for Specifying Macro References

Follow these rules when specifying macro references:

Comments and white-space characters (spaces, horizontal and
vertical tabs, new-line characters, and form feeds) may be used freely
within a macro reference anywhere that the delta symbol (<ucDelta
symbol>) appears in the following example:

Δ
nameΔ
(Δ
arg1Δ
,Δ
arg2Δ
)

Arguments consist of arbitrary text. Syntactically, they are not
restricted to C expressions. They may contain embedded comments and
white space. Comments are replaced with a single space. White space
(except for leading and trailing white space) is preserved during the
substitution.

The number of arguments in the reference must match the number of
parameters in the macro definition. Null arguments result in undefined
behavior.

Commas separate arguments except where the commas occur inside
string or character constants, comments, or pairs of parentheses.
Parentheses must be balanced within arguments.

8.1.2.3 Side Effects in Macro Arguments

It is not good programming practice to specify macro arguments that use
the increment (++), decrement ( -- ), and assignment operators (such as
+=) or other arguments that can cause side effects. For example, do not
pass the following argument to the
_toupper
macro:

_toupper(p++)

When the argument
p++
is substituted in the macro definition, the effect within the program
stream is as follows:

((p++) >= 'a' && (p++) <= 'z' ? (p++) & 0X5F : (p++))

Because
p
is being incremented, it does not have the same value for each
occurrence in this macro replacement. Even if you are aware of possible
side effects, the replacement lists within macro definitions can be
changed, which changes the side effects without warning.

8.1.3 Conversions to String Literals (#)

The
#
preprocessor operator is used to convert the argument that follows it
to a string literal. The preprocessor operator
#
can be used only in a function-like macro definition. For example:

The unary
#
operator produces a string from its operand. This example also uses the
fact that adjacent string literals are concatenated. If the operand to
#
contains double quotes or escape sequences, they are also expanded. For
example:

The preprocessor converts the line
printf("%d", glue(whole,num));
into
printf("%d", wholenum);
, and when executed, the program prints 5000. If the result is not a
valid token, an error occurs when the tokens are concatenated.

In HP C, the
##
operator is evaluated before any
#
operators on the line.
##
and
#
operators group left-to-right.

Six directives are available to control conditional compilation. They
delimit blocks of program text that are compiled only if a specified
condition is true. These directives can be nested. The program text
within the blocks is arbitrary and may consist of preprocessor
directives, C statements, and so on. The beginning of the block of
program text is marked by one of three directives:

#if

#ifdef

#ifndef

Optionally, an alternative block of text can be set aside with one of
two directives:

#else

#elif

The end of the block or alternative block is marked by the
#endif
directive.

If the condition checked by
#if
,
#ifdef
, or
#ifndef
is true (nonzero), then all lines between the matching
#else
(or
#elif
) and an
#endif
directive, if present, are ignored.

If the condition is false (0), then the lines between the
#if
,
#ifdef
, or
#ifndef
and an
#else
,
#elif
, or
#endif
directive are ignored.

8.2.1 The #if Directive

The
#if
directive has the following syntax:

#if constant-expression newline

This directive checks whether the constant-expression is true
(nonzero). The operand must be a constant integer expression that does
not contain any increment (++), decrement ( -- ),
sizeof
, pointer (*), address (&), and cast operators.

Identifiers in the constant expression either are or are not macro
names. There are no keywords, enumeration constants, and so on. The
constant expression can also include the
defined
preprocessing operator (see Section 8.2.7).

The constant expression in an
#if
directive is subject to text replacement and can contain references to
identifiers defined in previous
#define
directives. The replacement occurs before the expression is evaluated.
Each preprocessing token that remains after all macro replacements have
occurred is in the lexical form of a token.

If an identifier used in the expression is not currently defined, the
compiler treats the identifier as though it were the constant zero.

8.2.2 The #ifdef Directive

The
#ifdef
directive has the following syntax:

#ifdef identifier newline

This directive checks whether the identifier is currently defined.
Identifiers can be defined by a
#define
directive or on the command line. If such identifiers have not been
subsequently undefined, they are considered currently defined.

8.2.3 The #ifndef Directive

The
#ifndef
directive has the following syntax:

#ifndef identifier newline

This directive checks to see if the identifier is not currently defined.

8.2.4 The #else Directive

The
#else
directive has the following syntax:

#else newline

This directive delimits alternative source text to be compiled if the
condition tested for in the corresponding
#if
,
#ifdef
, or
#ifndef
directive is false. An
#else
directive is optional.

8.2.5 The #elif Directive

The
#elif
directive has the following syntax:

#elif constant-expression newline

The
#elif
directive performs a task similar to the combined use of the
else-if
statements in C. This directive delimits alternative source lines to be
compiled if the constant expression in the corresponding
#if
,
#ifdef
,
#ifndef
, or another
#elif
directive is false and if the additional constant expression presented
in the
#elif
line is true. An
#elif
directive is optional.