Previous | Contents | Index |
Preprocessor directives and macro references have syntax that is independent of the C language. Follow these rules when specifying macro definitions:
<ucDelta symbol> #<ucDelta symbol>define <ucDelta symbol> name(<ucDelta symbol>parm1<ucDelta symbol>,<ucDelta symbol>parm2<ucDelta symbol>)<ucDelta symbol>\ <ucDelta symbol>token-string<ucDelta symbol> |
Follow these rules when specifying macro references:
<ucDelta symbol>name<ucDelta symbol>(<ucDelta symbol>arg1<ucDelta symbol>,<ucDelta symbol>arg2<ucDelta symbol>) |
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:
#include <stdio.h> #define PR(id) printf("The value of " #id " is %d\n", id) main() { int i = 10; PR(i); } |
The output produced is:
The value of i is 10 |
The macro call expands in the following steps:
/*1*/ printf("The value of " #id " is %d\n", id) /*2*/ printf("The value of " "i" " is %d\n", 10) /*3*/ printf("The value of i is %d\n", 10) |
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:
#include <stdio.h> #define M(arg) printf(#arg " is %s\n", arg) main() { M("a\nb\tc"); } |
The macro call expands using the following steps:
/*1*/ printf(#arg " is %s\n", arg) /*2*/ printf("\"a\\nb\\tc\"" " is %s\n", "a\nb\tc"); /*3*/ printf("\"a\\nb\\tc\" is %s\n", "a\nb\tc"); |
The ## preprocessor operator is used to concatenate two tokens into a third valid token, as in the following example:
#define glue(a,b) a ## b main() { int wholenum = 5000; printf("%d", glue(whole,num)); } |
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 DEC C, the ## operator is evaluated
before any # operators on the line. ## and #
operators group left-to-right.
8.2 Conditional Compilation (#if, #ifdef, #ifndef, #else, #elif, #endif, and defined)
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:
Optionally, an alternative block of text can be set aside with one of two directives:
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.
8.2.6 The #endif Directive
The #endif directive has the following syntax:
#endif newline |
This directive ends the scope of the #if, #ifdef, #ifndef, #else, or #elif directive.
The number of necessary #endif directives changes according to whether the elif or #else directive is used. Consider the following equivalent examples:
#if true #if true . . . . . . #elif true . . #else . #if false . . #endif . . #endif #endif |
Another way to verify that a macro is defined is to use the defined unary operator. The defined operator has one of the following forms:
defined name |
defined (name) |
An expression of this form evaluates to 1 if name is defined and to 0 if it is not.
The defined operator is especially useful for checking many macros with just a single use of the #if directive. In this way, you can check for macro definitions in one concise line without having to use many #ifdef or #ifndef directives.
For example, consider the following macro checks:
#ifdef macro1 printf( "Hello!\n" ); #endif #ifndef macro2 printf( "Hello!\n" ); #endif #ifdef macro3 printf( "Hello!\n" ); #endif |
Another use of the defined operator is in a single #if directive to perform similar macro checks:
#if defined (macro1) || !defined (macro2) || defined (macro3) printf( "Hello!\n" ); #endif |
Note that defined operators can be
combined in any logical expression using the C logical operators.
However, defined can only be used in the
evaluated expression of an #if or #elif preprocessor directive.
8.3 File Inclusion (#include)
The #include directive inserts the contents of a specified file into the text stream delivered to the compiler. Usually, standard headers and global definitions are included in the program stream with the #include directive. This directive has two forms:
#include "filename" newline |
#include <filename> newline |
The format of filename is platform-dependent. If the filename is enclosed in quotation marks, the search for the named file begins in the directory where the file containing the #include directive resides. If the file is not found there, or if the file name is enclosed in angle brackets (< >), the file search follows platform-defined search rules. In general, the quoted form of #include is used to include files written by users, while the bracketed form is used to include standard library files.
See your platform-specific DEC C documentation for information on the search path rules used for file inclusion.
Macro substitution is allowed within the #include preprocessor directive.
For example, the following two directives can be used to include a file:
#define macro1 "file.ext" #include macro1 |
Defined macros used in #include directives must evaluate to one of the two following acceptable #include file specifications or an error is reported:
"filename" |
<filename> |
An included file may itself contain #include directives. Although the DEC C
compiler imposes no inherent limitation on the nesting level of
inclusion, the permitted depth depends on hardware and operating system
restrictions.
8.4 Explicit Line Numbering (#line)
The compiler keeps track of information about line numbers in each file involved in the compilation, and uses the line number when issuing diagnostic messages to the terminal or, when compiling in batch mode, to a log file.
The #line directive can be used to alter the line numbers assigned to source code. This directive gives a new line number to the following line, which is then incremented to derive the line number for subsequent lines. The directive can also specify a new file specification for the program source file. The #line directive does not change the line numbers in your compilation listing, only the line numbers given in diagnostic messages sent to the terminal screen or log file. This directive is useful for referring to original source files that are preprocessed into C code.
The #line directive has three forms:
#line integer-constant newline |
#line integer-constant "filename" newline |
#line pp-tokens newline |
In the first two forms, the compiler gives the line following a #line directive the number specified by the integer constant. The optional filename in quotation marks indicates the name of the source file that the compiler will provide in its diagnostic messages. If the file name is omitted, the file name used is the name of the current source file or the last file name specified in a previous #line directive.
In the third form, macros in the #line
directive are expanded before it is interpreted. This allows a macro
call to expand into the integer-constant, filename,
or both. The resulting #line directive
must match one of the other two forms, and is then processed as
appropriate.
8.5 Implementation-Specific Preprocessor Directive (#pragma)
The #pragma directive is a standard method for implementing platform-dependent features. This directive has the following syntax:
#pragma pp-tokensopt newline |
The supported pragmas vary across platforms. All unrecognized pragmas are diagnosed with an informational message. See your platform-specific DEC C documentation for a list of supported pragmas.
Some pragma directives are subject to macro expansion. A macro reference can occur anywhere after the keyword pragma. For example:
#define opt inline #define f func #pragma opt(f) |
After both macros are expanded, the #pragma directive becomes #pragma inline (func).
The following pragmas are subject to macro expansion:
builtins inline linkage standard dictionary noinline module nostandard extern_model member_alignment message use_linkage extern_prefix nomember_alignment |
The following pragmas are also subject to macro expansion, primarily for use in preprocess-only mode (that is, with the /PREPROCESS_ONLY qualifier on OpenVMS systems or the -E switch on Digital UNIX systems), and are not normally used when generating an object module with the DEC C compiler:
Macro expansion is a feature of pragmas introduced in early versions of DEC C and is retained for backward compatibility. Pragmas added in more recent versions of the compiler and pragmas added in the future have changed that practice to conform to the defacto industry standard of not performing macro expansion. (ANSI C places no requirement on macro expansion of pragmas.) |
The following describes how the compiler decides whether or not to macro-expand a given pragma:
In compilation modes other than /STANDARD=COMMON (OpenVMS systems) or -std0 (Digital UNIX systems), do Step 1:
Step 1:
In all compilation modes, do Step 2:
Step 2:
The test for matching a known pragma permits an optional double leading underscore. For example, #pragma __nostandard is equivalent to #pragma standard.
Example
The following example illustrates that for pragmas coded directly with a name that matches a known pragma, the macro-expansion behavior is generally the same in all modes and is backward-compatible. It is only in cases where a pragma was coded with a name that was not the name of a known pragma, expecting macro expansion to produce the pragma name, that backward-compatibility is broken, and then only in common mode. The exception is made in common mode to maintain compatibility with the Digital UNIX preprocessor.
#define pointer_size error #define m1 e1 #define e1 pointer_size 32 #define standard message #define x disable(all) #define disable(y) enable(y) #pragma pointer_size 32 /* In common mode, Step 1 skipped. In other modes, Step 1 finds that pointer_size is known not to expand. In any mode, Step 2 finds pointer_size is not a pragma requiring expansion. */ #pragma m1 /* In common mode, Step 1 skipped. In other modes, Step 1 expands m1 to pointer_size 32. In common mode, Step 2 finds m1 is not a pragma requiring expansion. In other modes, Step 2 finds pointer_size is not a pragma requiring expansion. */ #pragma standard x /* In common mode, Step 1 skipped. In other modes, Step 1 expands to message x. In common mode, Step 2 expands to message enable(all). In other modes, Step 2 expands message x to message enable(all). */ |
Previous | Next | Contents | Index |