Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Macros and postincrement

Here's some more weird macro behavior I was hoping somebody could shed 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++));
}

The output is 4 6 5. The value of b is incremented twice but not before MAX displays its value. Can anybody please tell me why this is happening and how does one predict such behavior? (Another instance of why macros should be avoided!)

like image 274
Appster Avatar asked Sep 15 '11 14:09

Appster


3 Answers

Macros do text substitution. Your code is equivalent to:

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

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

But as with any UB, if you stare at it for a while you might be able to come up with an explanation of what your implementation has actually done to yield the result you see. Order of evaluation of arguments is unspecified, but it looks to me as though the arguments have been evaluated in right-to-left order. So first, a and b are incremented once. a is not greater than b, so b is incremented again and the result of the conditional expression is 5 (that is to say, b after the first increment and before the second).

This behavior is not reliable - another implementation or the same implementation on another day might give different results due to evaluating the arguments in a different order, or theoretically might even crash because of the sequence point issue.

like image 166
Steve Jessop Avatar answered Oct 22 '22 08:10

Steve Jessop


In macro, parameters are just replaced by the arguments; so arguments can be evaluated multiple times if they are present multiple times in the macro.

Your example:

MAX(a++,b++)

Expands to this:

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

I think you don't need more explanations :)

You can prevent this by assigning each parameter to a temporary variable:

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

(This one uses several GCC extensions, though)

Or use inline functions:

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

This will be as good as a macro at runtime.

Or don't do the increments in the macro arguments:

a++;
b++;
MAX(a, b)
like image 43
Arnaud Le Blanc Avatar answered Oct 22 '22 08:10

Arnaud Le Blanc


When the preprocessor reads the line it replace the MAX(a++,b++) in the printf to the (a++>b++?a++;b++)

So your function becomes

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

Here order of evaluation is "compiler dependent".

To understand when these conditions can occur u have to understand about Sequence point.

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

    a[i] = i++;

because there is no sequence point specified for the assignment, increment or index operators, you don't know when the effect of the increment on i occurs. “Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.”. If a program breaks these rules, the results on any particular implementation are entirely unpredictable(undefined).

--The sequence points laid down in the Standard are the following:

1) The point of calling a function, after evaluating its arguments.

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

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

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

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

6)Completing the evaluation of a full expression. They are the following:

Evaluating the initializer of an auto object.

The expression in an ‘ordinary’ statement—an expression followed by semicolon.

The controlling expressions in do, while, if, switch or for statements.

The other two expressions in a for statement.

The expression in a return statement.

like image 2
Anurag Avatar answered Oct 22 '22 07:10

Anurag