about postfix operator and sequence point

void func(int a){
printf("%d",a);
}

int main(){
  int a= 0;
  printf("%d", a);
  func(a++);
}

This is my code BUT I can't understand why the result is 0 I think the result has to be 1

Because :

The side effect of updating the stored value of the operand shall occur between the previous and the next sequence point.

"a has to be increased before next sequence point"

All side effects of argument expression evaluations are sequenced before the function is entered

"There's sequence point before function is called"

So Isn't the variable a to be increased before func is called?

Can you tell me what am I understanding wrong?

THANK YOU

4 answers

  • answered 2021-04-21 16:28 dbush

    The postfix ++ operator evaluates to the current value of its operand, with the increment being a side effect.

    So since a is 0 to start, that's the value that is passed to the function.

  • answered 2021-04-21 16:31 Eric Postpischil

    func(a++) does not pass a to the function. It passes the value of the expression a++. The value of that expression is defined to be the value of a before the increment occurs.

    It is entirely irrelevant when the increment occurs. The value of a++ is the value of a prior to the increment.

  • answered 2021-04-21 16:34 Zoso

    Quoting sequencing rules (emphasis mine)

    1. When calling a function (whether or not the function is inline and whether or not function call syntax was used), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body.

    So, there's a sequence point after the evaluation of the function arguments. Now, this read with

    1. Between the previous and next sequence point a scalar object must have its stored value modified at most once by the evaluation of an expression, otherwise the behavior is undefined.

    will explain what is happening. The evaluation of the argument will happen and then there's a sequence point. The previous sequence point was the previous statement. So, evaluation of a++ will yield 0 since it's the postfix operator. This is what is passed to the function and hence the value 0.

    Here's confirming what's happening via assembly for the code

    void foo(int a) {}
    
    int main()
    {
        int a = 0;
        foo(a++);
        return 0;
    }
    

    which yields the assembly:

    foo:
            push    rbp
            mov     rbp, rsp
            mov     DWORD PTR [rbp-4], edi
            nop
            pop     rbp
            ret
    main:
            push    rbp
            mov     rbp, rsp
            sub     rsp, 16
            mov     DWORD PTR [rbp-4], 0
            mov     eax, DWORD PTR [rbp-4]
            lea     edx, [rax+1]
            mov     DWORD PTR [rbp-4], edx
            mov     edi, eax
            call    foo
    

    0 is moved at the memory location with the value of rbp-4. Then that value is moved to the register eax. mov edi, eax moves this value to the edi register which is actually used by the foo function, called via call foo.

    lea     edx, [rax+1]
    mov     DWORD PTR [rbp-4], edx
    

    These 2 instructions increment and store the value of a.

  • answered 2021-04-21 17:20 Ian Abbott

    Consider the following program:

    #include <stdio.h>
    
    int a; /* global */
    
    void func(int arg) {
        printf("func: arg=%d, (global) a=%d\n", arg, a);
    }
    
    int main(void){
        a = 0;
        printf("main: (global) a=%d\n", a);
        func(a++);
        printf("main: (global) a=%d\n", a);
    }
    

    That program uses a global variable a. In main, the expression statement func(a++); is evaluated in the following order:

    1. The arguments of func are evaluated in no particular order:
      1. For func's arg parameter, the argument expression a++ is evaluated yielding the old value of a (the value 0) with the side effect of a being incremented before the next sequence point.
    2. Any remaining side effects of argument evaluation are completed (so a will be incremented to 1). (This is the sequence point after the evaluation of the function designator and function arguments but before the actual call.)
    3. func is actually called.

    (I have omitted some details, such as evaluation of the function designator func.)

    The result of the above is that the old value of a (value 0) is passed to the arg parameter of func, but a will have been incremented (to value 1) when the body of function func is executed.

    The above behavior can be seen in the program's output:

    main: (global) a=0
    func: arg=0, (global) a=1
    main: (global) a=1