Preprocessor

 

The preprocessor analyzes the source file before the compiler proper sees the code. Here is what the preprocessor does:

  1. It replaces trigraph sequences by their equivalents (refer to the section “Compound Statements”).
  2. It joins any lines that end with a backslash character (\) together into a single line.
  3. It divides the program into a stream of tokens.
  4. It removes comments, replacing them by a single space.
  5. It processes preprocessor directives (see the section “Preprocessor Directives”) and expands macros.

Trigraph Sequences

To 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):


TrigraphMeaning

??=
#
??(
[
??)
]
??<
{
??>
 }
??/
\
??’
^
??!
|
??-
~

 

Preprocessor Directives

All 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