Macros and Post-Increment

Here are some more weird macros, I was hoping someone could shed some light on:

#define MAX(a,b) (a>b?a:b) void main(void) { int a = 3, b=4; printf("%d %d %d\n",a,b,MAX(a++,b++)); } 

Output 4 6 5. The value of b doubles, but not before MAX displays its value. Can someone please tell me why this is happening and how to predict this behavior? (Another example why macros should be avoided!)

+4
source share
8 answers

Macros replace text. Your code is equivalent to:

 printf("%d %d %d\n",a,b, a++ > b++ ? a++ : b++); 

This behavior is undefined because b potentially incremented (at the end of the third argument) and then used (in the second argument) without an intermediate point in the sequence.

But, as is the case with any UB, if you look at it for a while, you can come up with an explanation of what your implementation really did to give the result that you see. The order of evaluation of the arguments is unspecified, but it seems to me that the arguments were evaluated in order from right to left. So, first a and b increase once. a not greater than b , therefore b increases again and the result of the conditional expression is 5 (i.e., b after the first increment and before the second).

This behavior is not reliable - a different implementation or the same implementation on another day may give different results due to evaluating the arguments in a different order, or theoretically it may even fail due to a problem with the sequence point.

+6
source

In a macro, parameters are simply replaced by arguments; therefore, arguments can be evaluated several times if they are present several times in a macro.

Your example:

 MAX(a++,b++) 

Expands to:

 a++>b++?a++:b++ 

I think you no longer need explanations :)

This can be prevented by assigning a temporary variable to each parameter:

 #define MAX(a,b) ({ \ typeof(a) _a = a; \ typeof(b) _b = b; \ a > b ? a : b; \ }) 

(However, it uses several GCC extensions)

Or use the built-in functions:

 int MAX(int a, int b) { return a > b ? a : b; } 

It will be as good as a macro at runtime.

Or do not increment the macro arguments:

 a++; b++; MAX(a, b) 
+2
source

When the preprocessor reads a line, it replaces MAX (a ++, b ++) in printf with (a ++> b ++? A ++; b ++)

So your function becomes

  printf(a,b,(a++>b++?a++;b++)); 

Here the evaluation order is "compiler dependent".

To understand when these conditions may arise, you need to understand the point of the sequence.

At each point in the sequence, side effects of all previous expressions will be completed (all variable calculations will be completed). This is why you cannot rely on expressions such as:

  a[i] = i++; 

since for the assignment, increment, or index operators there is no specified point in the sequence, you do not know when the increment effect on i occurs. “Between the previous and the next point in the sequence, the object must have a value that its stored value has been changed no more than once by evaluating the expression. In addition, the previous value should only be read to determine the value to be stored.” If a program violates these rules, the results for any particular implementation are completely unpredictable (undefined).

- The sequence points specified in the Standard are as follows:

1) The function call point after evaluating its arguments.

2) The end of the first operand && Operator.

3) The end of the first operand || Operator.

4) The end of the first operand of the conditional operator:

5) The end of each operand of a comma operator.

6) Completion of the evaluation of the full expression. They are as follows:

Initializer evaluation of an automatic object.

An expression in a “regular expression” is an expression followed by a semicolon.

Control expressions in do, while, if, switch or for statements.

The other two expressions in the for statement.

The expression in the return statement.

+2
source

Macros are evaluated by a preprocessor, which foolishly replaces everything according to the definitions of macros. In your case, MAX(a++, b++) becomes (a++>b++) ? a++ : b++ (a++>b++) ? a++ : b++ .

+1
source

If I'm right, this happens:

with MAX replaced by (a> b ...) you have printf ("% d% d% d \ n", a, b, (a ++> b ++? a ++: b ++)) ;

First, a ++> b ++ is checked and both values ​​increase (a = 4, b = 5). Then the second b ++ is activated, but since it launches, it increases after the second value b = 5 is printed.

Sorry for my bad english, but I hope you understand that?!: D

Greece from Germany; -)

Ralph

+1
source

So your extension gives (adjusted for clarity):

 (a++ > b++) ? a++ : b++ 

... therefore, it is evaluated first (a++ > b++) , each time indicating one step and choosing a branch based on the values ​​of a and b not yet increased. The expression "else", b++ , is b++ , which performs the second increment by b , which has already been increased in the test expression. Since this is a post-increment, the value of b before the second increment is given by printf() .

+1
source


There are two reasons for the result you get here:

  • A macro is nothing more than code that expands and is inserted during compilation. So your macro

     MAX(a,b) (a>b?a:b) 

    becomes this

     a++>b++?a++:b++ 

    and your resulting functions are as follows:

     printf("%d %d %d\n",a,b, a++>b++?a++:b++); 

    Now it should be clear why b doubles: first for comparison, the second when it is returned. This is not undefined behavior, it is clearly defined, just analyze the code and you will see what exactly to expect. (this is predictable in some way)

  • The second problem is that the C parameters are passed from the last to the first on the stack, so all increments will be executed before you print the original values ​​a and b, even if they are listed first. Try this line of code and you will understand what I mean:

     int main(void) { int a = 3, b=4; printf("%d %d %d\n",a,b, b++); return 0; } 

The output will be 3 5 4. I hope this explains the behavior and helps you predict the result.
BUT , the last point depends on your compiler

0
source

I think the questionnaire expected the output to start:

 3 4 ... 

instead:

 4 6 ... 

and this is due to the fact that the parameters are evaluated from right to left when they are pushed onto the stack, that is, the last parameter is first evaluated and pushed, then the second - the last, then the second parameter and, finally, the first parameter.

I think (and someone sends a comment if it is wrong) that this is defined in the C standard (and C ++).

Update

The rating order is defined, but it is defined as undefined (thanks to Steve). Your compiler just does it like this. I think I am confused between the order of evaluation and the order of passing parameters.

-1
source

All Articles