5

I am trying to learn the basics of C/C++ right now. I am going through a course on Lynda.com

My questions deals with a sequence of code from Chapter 4 "Macro caveats from the Course C/C++ Essential Training". I have followed all the setup procedures to get Xcode and Eclipse setup correctly on a Mac and Eclipse on a PC. When I run this code on a MAC and PC I get different results. Just trying to understand why that is happening and what I can do to get the same result on both.

Here is the code:

// working.c by Bill Weinman <http://bw.org/>

#include <stdio.h>
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )

int increment() {
    static int i = 42;
    i += 5;
    printf("increment returns %d\n", i);
    return i;
}

int main( int argc, char ** argv ) {
    int x = 50;
    printf("max of %d and %d is %d\n", x, increment(), MAX(x, increment()));
    printf("max of %d and %d is %d\n", x, increment(), MAX(x, increment()));
    return 0;
}

On a PC I get this result:

increment returns 47
increment returns 52
max of 50 and 52 is 50
increment returns 57
increment returns 62
increment returns 67
max of 50 and 67 is 62

On a MAC (both Xcode and Eclipse) I get this result:

increment returns 47
increment returns 52
increment returns 57
max of 50 and 47 is 57
increment returns 62
increment returns 67
increment returns 72
max of 50 and 62 is 72

Why is this happening and what can I do to make sure the results are the same?

Slackjaw
  • 51
  • 2
  • 1
    BTW: The IDE does not mean anything, the exact compiler and the options you give it, will in just about every case determine the order. Though it might in theory change on each execution of the program. – Deduplicator Dec 05 '14 at 17:46
  • 1
    @AdrianoRepetti almost definitely a dup but since I have an answer on that question I don't want to dup hammer close it. – Shafik Yaghmour Dec 05 '14 at 17:57

3 Answers3

8

You have unspecified results here.

Order of evaluation within printf() is not defined. Once you have multiple increment() calls within the same printf then you never know which once gets executed first and how they are evaluated.

Gopi
  • 19,784
  • 4
  • 24
  • 36
6

The order in which all elements in a full-expression get evaluated is not defined. All that is required is that each sub-expression have its operands fully evaluated before it is evaluated. In the case of a function call, the arguments can be evaluated in any order, and in fact one argument might only be partially evaluated at the moment another argument becomes fully evaluated.

First, let's expand the macro:

printf("max of %d and %d is %d\n", x,
                                   increment(),
                                   ((x) > (increment()) ? (x) : (increment()));

(Oops, there is another problem here: if increment() is larger than x then it gets called again. Make the MAX macro a function instead so the arguments are only evaluated once!)

All of the following sequences are possible. I omit the evaluation of x here because it doesn't change.

  • The second argument increment() is evaluated, followed by x > increment(), finally followed by whichever ?: operand is selected. (This is likely the sequence you were expecting.)
  • x > increment() is evaluated, followed by whichever ?: operand is selected, finally followed by the second argument increment().
  • x > increment() is evaluated, followed by the second argument increment(), finally followed by whichever ?: operand is selected.

These may all yield different results, and they are all a correct interpretation of your code.

When you call multiple functions in a single full-expression, you should ensure that these functions either don't have any side-effects, or that the side-effects of each function do not change the behavior of any of the other functions. Otherwise, compiling on a different compiler (or a different version of the same compiler!) could change the result.

As an additional example, even the simple-looking expression increment() > increment() has an unspecified result, because the order in which the operands are evaluated is not defined; if the left operand is evaluated first then the result will be false, otherwise it will be true.

In the more complicated example ((a + b) * (c + d)) the compiler can evaluate a, b, c, and d in any order that it pleases. All that is required is that a and b must be evaluated before a + b can be, c and d must be evaluated before c + d can be, and a + b and c + d must be evaluated before the final operator * can be.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
2

There are two problems here.

You are relying on whatever the compiler chooses for evaluating the arguments to printf(). The statements evaluated first modify the values of later statements. Move the increment() calls out of the argument list. Store the results of increment() in variables instead and pass those variables to printf().

Also, the macro expansion of MAX can cause either argument to be evaluated once or twice. So even on the same OS and compiler, you can get awkward results. To fix this, do the same as I suggested for storing the increment() results. Pass those variables to MAX().

Jonny D
  • 2,244
  • 15
  • 22