|
The preprocessor analyzes the source file before the compiler proper sees the code. Here is what the preprocessor does: - It replaces trigraph sequences by their equivalents (refer to the section “Compound Statements”).
- It joins any lines that end with a backslash character (\) together into a single line.
- It divides the program into a stream of tokens.
- It removes comments, replacing them by a single space.
- It processes preprocessor directives (see the section “Preprocessor Directives”) and expands macros.
Trigraph SequencesTo handle non-ASCII character sets, the following three-character sequences (called trigraphs) are recognized and treated specially wherever they occur inside a program (as well as inside character strings):
| | Trigraph | Meaning |
| ??=
| # | ??(
| [ | ??)
| ] | ??<
| { | ??> | } | ??/ | \ | ??’ | ^ | ??! | | | ??-
| ~ |
Preprocessor DirectivesAll preprocessor directives begin with the character #, which must be the first nonwhitespace character on the line.The # can be optionally followed by one or more space or tab characters. The #define Directive Format 1: #define name text This defines the identifier name to the preprocessor and associates with it whatever text appears after the first blank space after name to the end of the line. Subsequent use of name in the program causes text to be substituted directly into the program at that point. Format 2: #define name(param_1, param_2, ..., param_n) text The macro name is defined to take arguments as specified by param_1,param_2, ..., param_n, each of which is an identifier. Subsequent use of name in the program with an argument list causes text to be substituted directly into the program at that point, with the arguments of the macro call replacing all occurrences of the corresponding parameters inside text. If the macro takes a variable number of arguments, three dots are used at the end of the argument list.The remaining arguments in the list are collectively referenced in the macro definition by the special identifier __VA_ARGS__.As an example, the following defines a macro called myPrintf to take a variable number of arguments: #define myPrintf(...) printf (“DEBUG: “ __VA_ARGS__); Legitimate macro uses would include myPrintf (“Hello world!\n”); as well as myPrintf (“i = %i, j = %i\n”, i, j); If a definition requires more than one line, each line to be continued must end with a backslash character.After a name has been defined, it can be used anywhere in the file. The # operator is permitted in #define directives that take arguments and is followed by the name of an argument to the macro.The preprocessor puts double quotation marks around the actual value passed to the macro when it’s invoked.That is, it turns it into a character string. For example, the definition
#define printint(x) printf (# x “ = %d\n”, x) with the call printint (count); is expanded by the preprocessor into printf (“count” “ = %i\n”, count); or equivalently printf (“count = %i\n”, count); The preprocessor puts a \ character in front of any “ or \ characters when performing this stringizing operation. So, with the definition #define str(x) # x the call str (The string “\t” contains a tab) expands to the following: ”The string \”\\t\” contains a tab” The ## operator is also allowed in #define directives that take arguments. It is preceded (or followed) by the name of an argument to the macro.The preprocessor takes the value passed when the macro is invoked and creates a single token from the argument to the macro and the token that follows (or precedes) it. For example, the macro definition #define printx(n) printf (“%i\n”, x ## n ); with the call printx (5) produces the following: printf (“%i\n”, x5); The definition #define printx(n) printf (“x” # n “ = %i\n”, x ## n ); with the call printx(10) produces printf (“x10 = %i\n”, x10); after substitution and concatenation of the character strings. Spaces are not required around the # and ## operators. The #error Directive General Format: #error text ... The specified text is written as an error message by the preprocessor. The #if Directive Format 1: #if constant_expression ... #endif The value of constant_expression is evaluated. If the result is nonzero, all program lines up until the #endif directives are processed; otherwise, they are automatically skipped and are not processed by the preprocessor or the compiler. Format 2: #if constant_expression_1 ... #elif constant_expression_2 ... #elif constant_expression_n ... #else ... #endif If constant_expression_1 is nonzero, all program lines up until the #elif are processed and the remaining lines up to the #endif are skipped. Otherwise, if constant_expression_2 is nonzero, all program lines up until the next #elif are processed and the remaining lines up to the #endif are skipped. If none of the constant expressions evaluates to nonzero, the lines after the #else (if included) are processed. The special operator defined can be used as part of the constant expression, so #if defined (DEBUG) ... #endif causes the code between the #if and #endif to be processed if the identifier DEBUG has been previously defined (see also #ifdef in the next section).The parentheses are not necessary around the identifier, so #if defined DEBUG works just as well. The #ifdef Directive General Format: #ifdef identifier ... #endif If the value of identifier has been previously defined (either through a #define or with the -D command-line option when the program was compiled), all program lines up until the #endif are processed; otherwise, they are skipped.As with the #if directive, #elif and #else directives can be used with a #ifdef directive. The #ifndef Directive General Format: #ifndef identifier ... #endif If the value of identifier has not been previously defined, all program lines up until the #endif are processed; otherwise, they are skipped.As with the #if directive, #elif and #else directives can be used with a #ifndef directive. The #import Directive4 Format 1: #import “fileName” If the file specified by fileName has been previously included in the program, this statement is skipped. Otherwise, the preprocessor searches an implementation-defined directory or directories first for the file fileName.Typically, the same directory that contains the source file is searched first, and if the file is not found there, a sequence of implementation-defined standard places is searched.After it’s found, the contents of the file are included in the program at the precise point that the #import directive appears. Preprocessor directives contained within the included file are analyzed; therefore, an included file can itself contain other #import or #include directives.
Format 2: #import <fileName> If the file has not been previously included, the preprocessor searches for the specified file only in the standard places. Specifically, the current source directory is omitted from the search. The action taken after the file is found is otherwise identical to that described previously. In either format, a previously defined name can be supplied and expansion will occur. So, the following sequence works: #define ROOTOBJECT <NSObject.h> ... #import ROOTOBJECT The #include Directive This behaves the same way as #import except no check is made for previous inclusion of the specified header file. The #line Directive General Format: #line constant “fileName” This directive causes the compiler to treat subsequent lines in the program as if the name of the source file were fileName and as if the line number of all subsequent lines began at constant. If fileName is not specified, the filename specified by the last #line directive, or the name of the source file (if no filename was previously specified), is used. The #line directive is primarily used to control the filename and line number that are displayed whenever an error message is issued by the compiler. The #pragma Directive General Format: #pragma text This causes the preprocessor to perform some implementation-defined action. For example, under the pragma #pragma loop_opt(on) causes special loop optimization to be performed on a particular compiler. If this pragma is encountered by a compiler that doesn’t recognize the loop_opt pragma, it is ignored.
The #undef Directive General Format: #undef identifier The specified identifier becomes undefined to the preprocessor. Subsequent #ifdef or #ifndef directives behave as if the identifier were never defined. The # Directive This is a null directive and is ignored by the preprocessor. Predefined Identifiers The following identifiers are defined by the preprocessor: Identifier
| Meaning |
| __LINE__
| Current line number being compiled | __FILE__ | Name of the current source file being compiled | __DATE__ | Date the file is being compiled, in the format “Mmm dd yyyy” | __TIME__ | Time the file is being compiled, in the format “hh:mm:ss” | __STDC__ | Defined as 1 if the compiler conforms to the ANSI standard and 0 if not | __STDC_HOSTED__ | Defined as 1 if the implementation is hosted and 0 if not | | __STDC_VERSION__ | Defined as 199901L |
|