C45. C Macros

Macro Definitions
The macros that we've been using since are known as simple macros, because they have no parameters. The preprocessor also supports parameterized macros. We'll look first at simple macros, then at parameterized macros. After covering them separately, we'll examine properties shared by both.
Simple Macros
The definition of a simple macro (or object-like macro, as it's called in the C stan­dard) has the form
#define identifier replacement-list
replacement-list is any sequence of preprocessing tokens, which are similar to the tokens discussed before. Whenever we use the term "token"  it means "preprocessing token."

A macro's replacement list may include identifiers, keywords, numeric con­stants, character constants, string literals, operators, and punctuation. When it encounters a macro definition, the preprocessor makes a note that identifier repre­sents replacement-list; wherever identifier appears later in the file, the preproces­sor substitutes replacement-list.

The # Operator
Macro definitions may contain two special operators, # and ##. Neither operator is recognized by the compiler; instead, they're executed during preprocessing.

The # operator converts a macro argument into a string literal; it can appear only in the replacement list of a parameterized macro. (The operation performed by # is known as "stringization," a term that I'm sure you won't find in the dictio­nary.)

There are a number of uses for #; let's consider just one. Suppose that we decide to use the PRINT_INT macro during debugging as a convenient way to print the values of integer variables and expressions. The # operator makes it pos­sible for PRINT_INT to label each value that it prints.

The ## Operator
The ## operator can "paste" two tokens (identifiers, for example) together to form a single token. (Not surprisingly, the ## operation is known as "token-pasting") If one of the operands is a macro parameter, pasting occurs after the parameter has been replaced by the corresponding argument.
Predefined Macros
C has several predefined macros. Each macro represents an integer constant or string literal. As Table below shows, these macros provide information about the current compilation or about the compiler itself.
Line number of file being compiled
Name of file being compiled
Date of compilation (in the form "Mmm dd yyyy")
Time of compilation (in the form "hh:mm: ss")
1 if the compiler conforms to the C standard (C89 or C99)

Conditional Compilation
The C preprocessor recognizes a number of directives that support conditional compilation—the inclusion or exclusion of a section of program text depending on the outcome of a test performed by the preprocessor.

The #if and #endif Directives
Suppose we're in the process of debugging a program. We'd like the program to print the values of certain variables, so we put calls of printf in critical parts of the program. Once we've located the bugs, it's often a good idea to let the printf calls remain, just in case we need them later. Conditional compilation allows us to leave the calls in place, but have the compiler ignore them.
In general, the #if directive has the form
# if constant-expression
The #endif directive is even simpler

Miscellaneous Directives
we'll take a brief look at the #error, #line, and #pragma directives. These directives are more specialized than the ones we've already examined, and they're used much less frequently.
The #error Directive
The #error directive has the form
#error message
where message is any sequence of tokens. If the preprocessor encounters an terror directive, it prints an error message which must include message. The exact form of the error message can vary from one compiler to another; it might be something like
Error directive: message
or perhaps just
# error message
Encountering an terror directive indicates a serious flaw in the program; some compilers immediately terminate compilation without attempting to find other errors.
#error directives are frequently used in conjunction with conditional com­pilation to check for situations that shouldn't arise during a normal compilation. For example, suppose that we want to ensure that a program can't be compiled on a machine whose int type isn't capable of storing numbers up to 100,000. The largest possible int value is represented by the INT_MAX macro, so all we need do is invoke an #error directive if INT_MAX isn't at least 100.000.

The #pragma Directive
The #pragma directive provides a way to request special behavior from the com­piler. This directive is most useful for programs that are unusually large or that need to take advantage of the capabilities of a particular compiler. The #pragma directive has the form
#pragma tokens
where tokens are arbitrary tokens. #pragma directives can be very simple (a sin­gle token) or they can be much more elaborate.

No comments: