Using Arguments with define

I l @ ve RuBoard

Using Arguments with #define

By using arguments, you can create macros that look and act much like functions. A macro with arguments looks very similar to a function because the arguments are enclosed within parentheses. Listing 16.2 provides some examples that illustrate how such a macro function is defined and used. Some of the examples also point out possible pitfalls, so read them carefully .

Listing 16.2 The mac_arg.c program.
 /* mac_arg.c -- macros with arguments */ #include <stdio.h> #define SQUARE(X) X*X #define PR(X)   printf("The result is %d.\n", X) int main(void) {      int x = 4;      int z;      z = SQUARE(x);      PR(z);      z = SQUARE(2);      PR(z);      PR(SQUARE(x+2));      PR(100/SQUARE(2));      printf("x is %d\n.", x);      PR(SQUARE(++x));      printf("After incrementing, x is %x.\n", x);      return 0; } 

wherever SQUARE(x) appears in listing 16.2, it is replaced by x*x . this differs from the earlier examples in that you are free to use symbols other than x when you use this macro. The x in the macro definition is replaced by the symbol used in the macro call in the program. Therefore, SQUARE(2) is replaced by 2*2 , so the x really does act as an argument.

However, as you will soon see, a macro argument does not work exactly like a function argument. Here are the results of running the program. Note that some of the answers are different from what you might expect. Indeed, your compiler might not even give the same answer as what's shown here for the next to last line:

 The result is 16. The result is 4. The result is 14. The result is 100. x is 4. The result is 30. After incrementing, x is 6. 

The first two lines are predictable, but then you come to some peculiar results. Recall that x has the value 4 . This might lead you to expect that SQUARE(x+2) would be 6*6 , or 36 , but the printout says it is 14 , which sure doesn't look like a square to us! The simple reason for this misleading output is the one we have already stated ”the preprocessor doesn't make calculations; it just substitutes strings. Wherever the definition shows an x , the preprocessor substitutes the string x+2 . Therefore,

 x*x 

becomes

 x+2*x+2 

The only multiplication is 2*x . If x is 4 , then this is the value of this expression:

 4+2*4+2 = 4 + 8 + 2 = 14 

This example pinpoints an important difference between a function call and a macro call. A function call passes the value of the argument to the function while the program is running. A macro call passes the argument token to the program before compilation; it's a different process at a different time. Can the definition be fixed to make SQUARE(x+2) yield 36? Sure. You simply need more parentheses.

 #define SQUARE(x)  (x)*(x) 

Then SQUARE(x+2) becomes (x+2)*(x+2) , and you get the desired multiplication as the parentheses carry over in the replacement string.

This doesn't solve all the problems, however. Consider the events leading to the next output line:

 100/SQUARE(2) 

becomes

 100/2*2 

By the laws of precedence, the expression is evaluated from left to right:

 (100/2)*2   or   50*2   or  100 

This mix-up can be cured by defining SQUARE(x) as

 #define SQUARE(x)  (x*x) 

This produces 100/(2*2) , which eventually evaluates to 100/4 , or 25 .

To handle both of the previous two examples, you need this definition:

 #define SQUARE(x)  ((x)*(x)) 

The lesson here is to use as many parentheses as necessary to ensure that operations and associations are done in the right order.

Even these precautions fail to save the final example from grief .

 SQUARE(++x) 

becomes

 ++x*++x 

and x gets incremented twice, once before the multiplication and once afterward.

 ++x*++x = 5*6 = 30 

Because the order of operations is left open , some compilers render the product 6*5 . Yet other compilers might increment both terms before multiplication, yielding 6*6 . In all these cases, however, x starts with the value 4 and ends up with the value 6 even though the code looks as though x were incremented just once.

The simplest remedy for this problem is to avoid using ++x as a macro argument. In general, don't use increment or decrement operators with macros. Note that ++x would work as a function argument because it would be evaluated to 6 , and then the value 6 would be sent to the function.

Including the Macro Argument in a String

Under K&R C, you could define macros like the following:

 #define PSQR(X)  printf("The square of X is %d.\n", ((X)*(X))); 

Suppose you used the preceding macro like this:

 PSQR(8); PSQR(2 + 3); 

Then the output would be this:

 The square of 8 is 64. The square of 2 + 3 is 25. 

That is, the macro argument inside the double quotes would be replaced by the actual argument, contrary to the normal rule that tokens inside double quotes are left unaltered. ANSI C does not allow this substitution within quotes. Running the same code under ANSI C produces the following output:

 The square of X is 64. The square of X is 25. 

The X in the quoted string is treated as ordinary text, not as a token that can be replaced.

Suppose you do want to include the macro argument in a string. A new feature of ANSI C enables you to do that. If, say, x is a macro parameter, then #x is that parameter converted to a string. Listing 16.3 illustrates how this process works.

Listing 16.3 The subst.c program.
 /* subst.c -- substitute in string */ #include <stdio.h> #define PSQR(x) printf("The square of " #x " is %d.\n",((x)*(x))) int main(void) {      int y = 5;      PSQR(y);      PSQR(2 + 4);      return 0; } 

Here's the output:

 The square of y is 25. The square of 2 + 4 is 36. 

In the first call to the macro, #x was replaced by "y" , and in the second call it was replaced by "2 + 4" . ANSI C string concatenation then combined these strings with the other strings in the printf() statement to produce the final strings that were used.

I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

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