American National Standards Institute. ANSI C

Chapter 14 - Advanced Programming Topics

Visual C++ 6: The Complete Reference
Chris H. Pappas and William H. Murray, III
  Copyright 1998 The McGraw-Hill Companies

Macros
In Chapter 6, you learned how to use the #define preprocessor to declare symbolic constants. You can use the same preprocessor to define macros. A macro is a piece of code that can look and act just like a function.
The advantage of a properly written macro is in its execution speed. A macro is expanded (replaced by its #define definition) during preprocessing, creating inline code. For this reason, macros do not have the overhead normally associated with function calls. However, each substitution lengthens the overall code size.
Conversely, function definitions expand only once no matter how many times they are called. The trade-off between execution speed and overall code size can help you decide which way to write a particular routine.
There are other subtle differences between macros and functions that are based on when the code is expanded. These differences fall into three categories:
  In C, a function name evaluates to the address of where to find the subroutine. Because macros sit inline and can be expanded many times, there is no one address associated with a macro. For this reason, a macro cannot be used in a context requiring a function pointer. Also, you can declare pointers to functions, but you cannot declare a pointer to a macro.
  The compiler sees a function declaration differently from a #define macro. Because of this, the compiler does not do any type checking on macros. The result is that the compiler will not flag you if you pass the wrong number or wrong type of arguments to a macro.
  Because macros are expanded before the program is actually compiled, some macros treat arguments incorrectly when the macro evaluates an argument more than once.
Defining Macros
Macros are defined the same way you define symbolic constants. The only difference is that the substitution_string usually contains more than a single value:
#define search_string substitution_string
The following example uses the preprocessor statement to define both a symbolic constant and a macro to highlight the similarities:
/* #define symbolic constant */
#define iMAX_ROWS 100

/* #define macro             */
#define NL putchar(‘\n’)
The NL macro causes the preprocessor to search through the source code looking for every occurrence of NL and substituting it with putchar(‘\n’). Notice that the macro did not end with a semicolon. The reason for this has to do with how you invoke a macro in your source code:
int main(void)
{
    .
    .
    .
 NL;
The compiler requires that the macro call end with a semicolon if the substitution_string of the macro ends with a semicolon:
#define NL putchar(‘\n’);
After the macro expansion has taken place, the compiler would see the following code:
int main(void)
{
    .
    .
    .
 putchar(‘\n’);;
Macros and Parameters
Both C and C++ support macros that take arguments. These macros must be defined with parameters, which serve a purpose similar to that of a function’s parameters. The parameters act as placeholders for the actual arguments. The following example demonstrates how to define and use a parameterized macro:
/* macro definition */
#define READ_RESPONSE(c) scanf(“%c”,(&c))
#define MULTIPLY(x,y) ((x)*(y))

int main(void)
{
 char cresponse;
 int a = 10, b = 20;
    .
    .
    .
 READ_RESPONSE(cresponse); /* macro expansions */
printf(“%d”,MULTIPLY(a,b));
In this example x, y, and c serve as placeholders for a, b, and cresponse, respectively. The two macros, READ_RESPONSE and MULTIPLY, demonstrate the different ways you can invoke macros in your program. For example, MULTIPLY is substituted within a printf( ) statement, while READ_RESPONSE is stand-alone.
Problems with Macro Expansions
Macros operate purely by substituting one set of characters, or tokens, with another. The actual parsing of the declaration, expression, or statement invoking the macro occurs after the macro expansion process. This can lead to some surprising results if care is not taken. For example, the following macro definition appears to be perfectly legal:
#define SQUAREIT(x) x * x
If the statement is invoked with a value of 5, as in:
iresult = SQUAREIT(5);
the compiler sees the following statement:
iresult = 5 * 5;
On the surface, everything still looks okay. However, the same macro invoked with this next statement:
iresult = SQUAREIT(x + 1);
is seen by the compiler as:
iresult = x + (1 * x) + 1;
instead of:
iresult = (x + 1) * (x + 1);
As a general rule, it is safest to always parenthesize each parameter appearing in the body of the macro, as seen in the previous READ_RESPONSE and MULTIPLY macro definitions and under those circumstances where the macro expansion may appear in a cast expression; for example:
dresult = (double)SQUAREIT(x + 1);
It is best to parameterize the entire body of the macro:
#define SQUAREIT(x) ((x) * (x))
Most of the time, the compiler is insensitive to additional spacing within standard C and C++ statements. This is not the case with macro definitions. Look closely at this next example and see if you can detect the error:
/* incorrect macro definition */
#define BAD_MACRO (ans) scanf(“%d”,(&ans))
Remember that the #define preprocessor searches for the search_string and substitutes it with the substitution_string. These two strings are delineated by one or more blanks. The definition above, when expanded, will appear to the compiler as:
(ans) scanf(“%d”,(&ans));
This creates an illegal statement. The problem has to do with the space between the macro name BAD_MACRO and (ans). That extra space made the parameter list part of the substitution_string instead of its proper place in the search_string. To fix the BAD_MACRO definition, remove the extra space:
#define BAD_MACRO(ans) scanf(“%d”,(&ans))
To see if you really understand the hidden problems that you can encounter when using macros, see if you can determine what the following statement evaluates to:
int x = 5;
iresult = SQUAREIT(x++);
The situation gets worse when using certain C and C++ operators like increment, ++, and decrement, - -. The result of this expression may be 30, instead of the expected 25, because various compilers may evaluate the expression in several different ways. For example, the macro could be expanded syntactically to read:
/* iresult = x * x; */
iresult = 5 * 5;
or
/* iresult = x * (x+1); */
iresult = 5 * 6;
Creating and Using Your Own Macros
Macros can include other macros in their definitions. This feature can be used to streamline your source code. For example, look at the following progressive macro definitions:
#define NL putchar(‘\n’)
#define TAB putchar(‘\t’)
#define FORMAT1 NL, NL, TAB
#define FORMAT2 NL, TAB, TAB
#define BEGIN_PROMPT FORMAT1, printf(“Want to begin?”); \
                             printf(“\nType 1 for yes, 0 for no”)
#define READ_RESPONSE FORMAT2,scanf(“%d”,(&c))
#define FORMAT_PRINT(ccontrol,ivalue,fvalue) \
         printf(“\n%c\t%d\t%8.2f”,(ccontrol),(ivalue),(fvalue))
Now, instead of seeing all of the code defined in the macro, your program code takes on the following appearance:
int main(void)
{
 char cresponse;
 int ivalue = 23;
 float fvalue = 56.78;
    .
    .
    .
 BEGIN_PROMPT;
 READ_RESPONSE(cresponse);
 FORMAT_PRINT(cresponse,ivalue,fvalue);
Remember, however, that you trade off automatic compiler type checking for source code readability, along with possible side effects generated by invoking the statement’s syntax.
Macros Shipped with the Compiler
The ANSI C committee has recommended that all C compilers define five special macros that take no arguments. Each macro name begins and ends with two underscore characters as listed in Table 14-1.
Table 14-1: Predefined Macros
Macro Name
Meaning
__LINE__
A decimal integer constant representing the line number of the current source program line
__FILE__
A string constant representing the name of the current source file
__DATE__
A string constant representing the calendar date of the translation in the form “mmm dd yyyy”
__TIMESTAMP
A string constant representing the date and time of the last modification of the source file in the form “Ddd Mmm hh:mm:ss yyyy”
__STDC__
Represents a decimal 1 if the compiler is ANSI C compatible
Predefined macros are invoked the same way user-defined macros are invoked. For example, print your program’s name, date, and current line number to the screen with the following statement:
printf(“%s | %s | Line number: %d”,__FILE__,__DATE__,__LINE__);

Books24x7.com, Inc 2000 –  


Visual C++ 6(c) The Complete Reference
Visual Studio 6: The Complete Reference
ISBN: B00007FYGA
EAN: N/A
Year: 1998
Pages: 207

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net