Reputation: 1577
I am new to C programming, in fact programming at all, and recently learn the use of macros with regard to preprocessor directives. Although I am getting more familiar with it the following exercise that I got from a textbook stumbles me since I do not get the solution or the general "take-away lesson" from it.
Before I wrote this question here, I tried to execute the code myself by adding some printf()
in order to obtain the correct answers but it does not even compile. Now, before I write down the question and code I want to make it explicit that this is a self-learning question and that I do not want to offend people here with a question that many will find trivial. I just want to understand what is going on.
The code is as follows
int x=2, y=3, a=4,b=5;
#define MAX(x, y) x > y x : y
int c,d,e,f;
c = MAX( a, 3 );
d = MAX( y, x );
e = MAX( ++x, 1 );
f = MAX( b, MAX (6, 7) );
I am asked to give the values of c,d,e and f. There is an additional hint that although it is named that way the above macro is NOT the maximum operator. Therefore, I don't think the "obvious" guess of e.g. max(a,3) = 4 is correct. Consequently, I don't know what is going on.
EDIT: I forgot to mention: I know that there are parentheses missing for the correct use. But I am specifically asked to evaluate the terms without them. Therefore I am confused since I do not know exactly how the results change and the function behave without those included.
Upvotes: 0
Views: 1090
Reputation: 123568
Expanding the macro as-is, we get the following:
Original Expanded
-------- --------
c = MAX( a, 3 ); c = a>3 a : 3;
d = MAX( y, x ); d = y>x y : x;
e = MAX( ++x, 1 ); e = ++x>1 ++x : 1;
f = MAX( b, MAX (6, 7) ); f = b>MAX (6, 7) b : MAX (6, 7);
f = b>6>7 6 : 7 b : 6>7 6 : 7;
Macro expansion is just dumb text substitution - syntax, scope, precedence, associativity, values, side effects, etc., are simply not taken into account when a macro is expanded (macro expansion occurs before the source code is fed to the compiler proper).
Obviously, none of the expanded expressions above will compile. For that to happen, we need to fix the MAX
macro by defining it as
#define MAX( x, y ) x>y ? x : y
Now we get the following:
Original Expanded
-------- --------
c = MAX( a, 3 ); c = a>3 ? a : 3;
d = MAX( y, x ); d = y>x ? y : x;
e = MAX( ++x, 1 ); e = ++x>1 ? ++x : 1;
f = MAX( b, MAX (6, 7) ); f = b>MAX (6, 7) ? b : MAX (6, 7);
f = b>6>7 ? 6 : 7 ? b : 6>7 ? 6 : 7;
Better. The expanded expressions above will compile, but the last two don't do anything like what you expect them to. e
won't get the max of x
and 1
, it will either get x+2
or 1
(?
introduces a sequence point, so the behavior isn't undefined). f
gets ... something, can't remember the associativity of >
and ?:
offhand, not really willing to dig it up.
Again, macros don't take precedence and associativity into account when expanding their arguments. Imagine we write a macro CUBE
that does the following:
#define CUBE(x) x * x * x
and we call it as
y = CUBE( 2 + 3 );
That expands to
y = 2 + 3 * 2 + 3 * 2 + 3;
which gives us the value 17, when we were probably expecting 125.
The right way to define the MAX
macro is
#define MAX( x, y ) ((x) > (y) ? (x) : (y))
We not only parenthesize each argument, we parenthesize the entire expression. This way, precedence and associativity are preserved not only if you pass complex expressions as arguments, but also if you pass this macro as an argument to another one (as in the last example):
Original Expanded
-------- --------
c = MAX( a, 3 ); c = ((a) > (3) ? (a) : (3));
d = MAX( y, x ); d = ((y) > (x) ? (y) : (x));
e = MAX( ++x, 1 ); e = ((++x) > (1) ? (++x) : (1));
f = MAX( b, MAX (6, 7) ); f = ((b) > (MAX (6, 7)) ? (b) : (MAX (6, 7)));
f = ((b) > ((6) > (7) ? (6) : (7)) ? (b) : ((6) > (7) ? (6) : (7)));
This time, f
will be evaluated like you expect it to be.
The evaluation of e
is still problematic (++x
can still be evaluated twice). This is a problem in general with macros that expand their arguments more than once - arguments that have side effects can be evaluated multiple times, which will lead to wrong answers or undefined behavior.
Upvotes: 2
Reputation: 9203
You mention that you don't know what is going on. There is only way - run only the preprocessor. What compiler are you using? Assuming gcc/clang you can -
gcc -E filename.c
This will only run the preprocessor and let you analyze what is going on.
BTW you code doesn't compile because you made a mistake with the ternary operator - should be x > y ? x : y
.
Upvotes: 0
Reputation: 104579
Incorrect use of ternary operator.
#define MAX(x,y) x>y x : y
Should be:
#define MAX(x,y) (x>y) ? x : y
And to allow for more complex expressions:
#define MAX(x,y) (((x)>(y)) ? (x) : (y))
Upvotes: 2