4

Here I have a function named max(a,b) to get the max number out of two. And I found that the value of variable a and b using printf() are different after executing

printf("maxab()=%d after max: a=%d b=%d \n",max(a++,b++),a,b);

when a and b are Global variables and Local variables. Below is my code:

#include<stdio.h>

int max(int a,int b)
{

    if(a>b)
    {
        //printf("In func max():%d %d \n",a,b);
        return a;
    }
    else {
        //printf("In func max():%d %d \n",a,b);
        return b;
    }

}
void jubu_test(void)
{
    int a=1;
    int b=2;    
    printf("maxab()=%d after max: a=%d b=%d \n",max(a++,b++),a,b);  //a=2,b=3
}
int c=2;
int d=1;
void quanju_test(void)
{
    printf("maxcd()=%d  c=%d d=%d \n",max(c++,d++),c,d);    //c=2,d=1
    c=2;
    d=1;
    int f=max(c++,d++);
    printf("maxcd()=%d after max: c=%d d=%d \n",f,c,d);     //c=3,d=2
}   
int main(int argc, char** argv)
{
    jubu_test();
    quanju_test();
}

The result I get on my computer is:

maxab()=2 after max: a=2 b=3
maxcd()=2  c=2 d=1
maxcd()=2 after max: c=3 d=2

My question is: Why in the second output a and b is their original value and why the third output is a+1 and b+1? Why when a and b are Global variables, the value of a and b printed out only changes when we execute max(a++,b++) first? Why when a and b are local variables it doesn't matter?

Thanks! (using gcc 5.3.0 on windows 10)

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
walle
  • 129
  • 1
  • 9
  • see http://stackoverflow.com/questions/949433/why-are-these-constructs-using-undefined-behavior – Mathieu Borderé Mar 30 '17 at 07:07
  • @LPs Interesting, I'd like to know the case where this particular code can invoke UB. Maybe i'm just being blind, but you mind elaborating a bit on this case? – Sourav Ghosh Mar 30 '17 at 07:11
  • 1
    @SouravGhosh Actually, I'm trying to find it out. After I proposed the duplicate I start thinking deeply on it. In this case incremented variables are different, so not the case like `i++` `+` `++i` BTW gcc shows a lot of warnings on sequence point on inc in function call – LPs Mar 30 '17 at 07:19
  • Thanks everyone! I will avoid such "Undefined behaviors" – walle Mar 30 '17 at 07:25
  • @LPs Right. Unless we figure out where is it exactly invoke UB, can we not have the comment to canonical UB post, please? :) – Sourav Ghosh Mar 30 '17 at 07:27
  • 1
    @SouravGhosh I retracted it. BTW [warnings are clear](http://rextester.com/YITQI57516)...if gcc is evaluating them correctly – LPs Mar 30 '17 at 07:28
  • @LPs That's what we need to find out, I'll check on it later today, and I was referring to the existing comment of yours. :) – Sourav Ghosh Mar 30 '17 at 07:30
  • 1
    Actually, this is UB and not just unspecified. – Lundin Mar 30 '17 at 07:48
  • @LPs Why I get no warning on my gcc? Thanks – walle Mar 30 '17 at 07:57
  • 1
    add `-Wall -Wextra and -pedantic-errors` and you'll see them. `-Wall` it is enough for your specific case. – LPs Mar 30 '17 at 08:01
  • @LưuVĩnhPhúc Yes indeed it is a duplicate of that one. (I already exhausted my dupe hammer on closing this incorrectly, as a duplicate to an order of evaluation question. Posted an answer to make up for my goof-up.) – Lundin Mar 30 '17 at 08:09
  • @LPs Thanks a lot ! – walle Mar 30 '17 at 08:10

2 Answers2

5

The expression printf(... max(a++,b++),a,b); is undefined behavior as per Why are these constructs (using ++) undefined behavior?.

The evaluation of a++ is not sequenced in relation to the evaluation of a, same for b++ and b. It doesn't matter that there is a sequence point before the function is called, because the sub-expressions may be evaluated before that, in any order.

Undefined behavior = always a bug. Means that the program could have any kind of behavior, print anything, crash & burn etc.

The reason why it is undefined behavior is this, C11 6.5/2:

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

(a is a scalar object - not an array or struct etc. a++ causes a side-effect of updating the variable. This is unsequenced in relation of the value computation of a elsewhere in the same expression.)


Not to be confused with unspecified behavior, which means that the program will behave deterministically but you can't know in which way. For example, the order of evaluation of function arguments is unspecified behavior:

int func (void)
{
  static int x=0;
  x++;
  return x;
}

printf("%d %d", func(), func());

This may print either 1 2 or 2 1 and we can't know or assume which applies. The compiler does not need to document this, nor does it have to behave consistently throughout the program. It could pick one order in one case and another order in another case.

Code relying on unspecified behavior is bad, but won't behave completely erratically like code containing undefined behavior.

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • It's indeed possible for order to be `a++` -> `a` -> call `max()`, which makes my earlier comment about sequence point a nonsense. In the end it always comes to undefined behaviour. +1 – user694733 Mar 30 '17 at 08:35
3

As I believe, it has nothing to do with the scope of the variables. C does not specify the exact order of the evaluation of function arguments. So, this is unspecified behaviour.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • This is true but it's still weird that it behaves differently in either case. What is the underlying reasoning (even if it isn't consistent amongst various C implementations)? – jakeehoffmann Mar 30 '17 at 07:00
  • 1
    @jakeehoffmann You would have to look assembly (which we don't have) to know for sure. But that seems pointless exercise since code is broken to begin with. – user694733 Mar 30 '17 at 07:14
  • 2
    @jakeehoffmann - The reason is the compilers static analysis of the code, where it concluded (possibly randomly) to evaluate the arguments in a different order every time. It's within its right, and why one should not depend on behavior that is unspecified (because unspecified, unlike implementation defined, means it doesn't have to be documented). – StoryTeller - Unslander Monica Mar 30 '17 at 07:15
  • @jakeehoffmann The underlying reason is to give compilers the freedom to evaluate their internal expression trees in any order they like, so that the generated code will be slightly faster. – Lundin Mar 30 '17 at 07:43
  • 1
    This `max(a++,b++),a,b` is also undefined behavior, because there is a side effect on updating `a` which is not sequenced in relation to `a` in the same expression, and the `a` is not used to determine which value to store. – Lundin Mar 30 '17 at 08:04