1

Code :

#include<stdio.h>
#include<stdlib.h>

int arr[] = {1, 2, 3, 4};
static int count = 0;

int incr( ) {
    ++count;
    ++count;
    return count;
}

int main(void)
{ 
    printf("\ncount= %d \n",count);
    int i;
    arr[count++]=incr( );
    for(i=0;i<4;i++)
       printf("arr[%d]=%d\n", i,arr[i]);
    printf("\nIncremented count= %d \n",count);
    return 0;
}

Output

count = 0  
arr[0]=2  
arr[1]=2  
arr[2]=3  
arr[3]=4  
Incremented count = 1  

The final incremented value of global variable count is 1 even though it has been incremented thrice.

When count++ is replaced by count in arr[count++]=incr( ) the final incremented value of count is 2.

Chris Maes
  • 35,025
  • 12
  • 111
  • 136
  • 2
    are you sure you recompiled your program before you got this output? I compiled it and I get the correct output (arr[0] = 3 and incremented count = 3) – Chris Maes Jun 02 '14 at 09:17
  • 2
    make sure you save your file and recompile; see if you get the same output – Chris Maes Jun 02 '14 at 09:18
  • The code is a bit hard to follow due to the side-effect of `incr` on `count`. Is this homework? – Codor Jun 02 '14 at 09:19
  • 2
    I don't quite understand the rain of down-votes, except maybe that the "explain the code" questions aren't the best. There is a `close` question dialog, and "cannot be reproduced" reason under off-topic, and no-one used it. IMO it's either as @ChrisMaes says or UB, which is either a close reason or pretty answerable. – luk32 Jun 02 '14 at 09:22
  • 2
    This is undefined behavior, very similar to this: http://stackoverflow.com/questions/15637678/sequence-point-after-a-return-statement ; the evaluation of `count++` and the call to `incr()` are unsequenced. Since both modify count, you get UB. – Mat Jun 02 '14 at 09:28
  • @kalanidhi ,Read the answer posted by Quentin and you will understand why your getting unexpected output – Spikatrix Jun 02 '14 at 09:44
  • @Mat But there is a sequence point after the point where the function parameters are evaluated (there are none), but before the function is executed...? – Lundin Jun 02 '14 at 15:26
  • @Lundin: there is, but it's useless here. You'd need a sequence point between the evaluation of the right hand side of the assignment and the evaluation of the left hand side of the assignment for this expression to be valid. There is no sequencing there, so the evaluation of the function itself (to get the return value) and the evaluation of `arr[count++]` (to determine where to store) are not sequenced. (Or something like that, I'm a bit tired :-) ) – Mat Jun 02 '14 at 16:49
  • @Mat The evaluation doesn't really matter: it can evaluate left or right hand first but that shouldn't cause this behavior. You'd get either 0 or 3, but never 1. What isn't clear here as far as the C standard is concerned, is when the ++ is executed. The standard only states "The value computation of the result is sequenced before the side effect of updating the stored value of the operand.". But clearly the ++ must be sequenced _before_ the next sequence point, which happens to be the function call. The compiler can't just change the whole meaning of the program and postpone the ++. – Lundin Jun 03 '14 at 06:18
  • So maybe I understood the whole function call thing wrong: there is never a sequence point there unless the function has parameters? Because if I modify the code to enforce a sequence point `arr[count++] = 0, incr();` then I get the expected result 3 instead of 1 (GCC on Windows). – Lundin Jun 03 '14 at 06:21
  • I guess this is why: C11 6.5.2.2/10 "There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call. **Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.**" 15 years of C programming and still learning... :) – Lundin Jun 03 '14 at 06:26
  • (Meaning this was a very good question and shouldn't get all those downvotes, +1) – Lundin Jun 03 '14 at 06:27
  • @ChrisMaes with gcc(both on windows and linux) I get the exact same output as that in the question so can you please tell the which compiler you are using ? – user3698485 Jun 04 '14 at 09:50
  • @user3698485 I'm on opensuse 13.1 environment with compiler: gcc (SUSE Linux) 4.8.1 20130909 [gcc-4_8-branch revision 202388] – Chris Maes Jun 04 '14 at 09:58
  • here's my compiler line (generated by eclipse). I confirm the same output I reported before. `gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"test.d" -MT"test.d" -o "test.o" "../test.c"` – Chris Maes Jun 04 '14 at 10:01
  • It is irrelevant to post which compiler you are using, since the code invokes undefined behavior and also relies on unspecified behavior. So the same code can behave differently from time to time, using the same compiler and same compiler options. – Lundin Jun 04 '14 at 10:38

2 Answers2

7

This is undefined behaviour from bad sequencing. On this line:

arr[count++]=incr( );

What happens (with your compiler) is:

  • arr[count] is resolved to arr[0], postfix ++ will be applied at the end of the statement;
  • incr() is called, count is now equal to 2, incr() returns 2;
  • arr[0] gets assigned 2;
  • postfix ++'s side effect kicks in, and count is now equal to 1. Previous changes to count are lost.

You will find more info on "side effects" and "sequence points" by googling their real name :)

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • I started writing a similar answer but I'm not so sure that this is actually UB... There is an issue with unsequenced order of evaluation here which leads to _unspecified behavior_. But since there is a sequence point when the function is called. `count` is never accessed twice between sequence points, and therefore I don't think this is UB. – Lundin Jun 02 '14 at 15:24
  • I must admit that sequence points are still a bit blurry for me. However, Chris Maes (first comment under OP) gets a different result for the same code. Plus, the way I understood (and transcribed) it seems to make sense, so... standard guru needed ;) – Quentin Jun 02 '14 at 15:37
  • 1
    the value of count in [count++] (count=0) is being carried in a register. the contents of that register are kept through/uneffected by the call to incr(). Then when the post increment operation is applied the 0 in the register is being incremented to 1 and saved to count. – user3629249 Jun 03 '14 at 02:49
  • Apparently, ordinary function calls aren't guaranteed to be sequenced in a certain way in relation to the caller. This was new to me, added an answer with the relevant quote from the standard. – Lundin Jun 03 '14 at 06:51
  • 1
    I am getting the same results as OP, while the first comment under his/her question gets other. So +1 for UB. – gsamaras Jun 03 '14 at 07:03
5

To understand why your code goes wrong, you must first understand undefined behavior and sequence points, which is a rather advanced topic. You also need to understand what undefined behavior is, and what unspecified behavior is, explained here.

If you do something to a variable which counts as a side-effect, such as modifying it, then you are not allowed to access that variable again before the next sequence point, for other purposes than to calculate which value to store in your variable.

For example i = i++ is undefined behavior because there are two side effects on the same variable with no sequence point in between. But i = i+1; is well-defined, because there is only one side effect (the assignment) and the i+1 is only a read access to determine what value to store.

In your case, there is no sequence point between the arr[count++] sub-expression and the incr() sub-expression, so you get undefined behavior.

This is how sequence points appear in functions, C11 6.5.2.2:

There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call. Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.

This means that the contents of the function aren't sequenced in relation to the rest of the expression. So you are essentially writing an expression identical to arr[count++] = ++count;, except through the function you managed to squeeze in two unsequenced ++count on the right side of the operation, which wouldn't otherwise be possible. Any any rate, it is undefined behavior.

Fix your code by enforcing sequence points between the left hand and the right hand of the expression. However, the order of evaluation of sub-expressions is unspecified behavior, so you need to ensure that your code is safe no matter if the left or right side is evaluated first. This code will fix the problems:

// artificial example, don't write code like this
0,arr[count++] = 0,incr();

since the comma operator introduces a sequence point. But of course, writing nonsense code like that isn't something you should be doing. The real solution is to never use ++ together with other operators in the same expression.

// good code, write code like this
arr[count] = incr();
count++;
Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396