175

After reading this answer about undefined behavior and sequence points, I wrote a small program:

#include <stdio.h>

int main(void) {
  int i = 5;
  i = (i, ++i, 1) + 1;
  printf("%d\n", i);
  return 0;
}

The output is 2. Oh God, I didn't see the decrement coming! What is happening here?

Also, while compiling the above code, I got a warning saying:

px.c:5:8: warning: left-hand operand of comma expression has no effect

  [-Wunused-value]   i = (i, ++i, 1) + 1;
                        ^

Why? But probably it will be automatically answered by the answer of my first question.

Community
  • 1
  • 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 293
    Don't do weird things, you'll have no friends :( – Maroun Jun 03 '15 at 08:15
  • 9
    The warning message is the answer to your first question. – Yu Hao Jun 03 '15 at 08:17
  • @MarounMaroun I didn't, I read it in an answer. :) Such a fine top answer by the way, I have already voted up! :P – gsamaras Jun 03 '15 at 08:17
  • @YuHao that was really a hint, but it still was not clear. That's why I got the downvotes? :/ – gsamaras Jun 03 '15 at 08:19
  • @KarolyHorvath I know this is off-topic, but I hope you aren't mad at me for this question: http://stackoverflow.com/questions/26716255/why-does-this-program-print-forked-4-times. I feel bad and was hoping to be able to tell you some day! – gsamaras Jun 03 '15 at 08:25
  • However, in this case `i = (i,++i,++i);` it does and invokes UB. You may also want haccks answer for this. – gsamaras Jun 03 '15 at 08:32
  • 2
    @gsamaras: nope. the resulting *value* is discarded, not the modification. the real answer: the comma operator creates a sequence point. – Karoly Horvath Jun 03 '15 at 08:36
  • Yes I agree @KarolyHorvath. I edited the comment, to avoid confusion. Hope you are ok with the `fork()` question... – gsamaras Jun 03 '15 at 08:38
  • 3
    @gsamaras You shouldn't care when you have positive score and even more with 10+ question. – LyingOnTheSky Jun 03 '15 at 16:43
  • 9
    Note: An optimizing compiler may simple do `printf("2\n");` – chux - Reinstate Monica Jun 03 '15 at 17:27
  • Well, maybe the downvoters see something we can't see LyingOnTheSky. However, they do not seem ready to share that info with us! :/ @chux, I do not understand how this is connected to my question. – gsamaras Jun 03 '15 at 22:11
  • 1
    @gsamaras I was noting that a smart compiler may evaluate `int i = 5; i = (i, ++i, 1) + 1; ...` completely at compile time with the result of only needing a print of `"2\n"` The evaluation need not occur at run time. Based on the good quality of answers, this is only tangentially relevant. – chux - Reinstate Monica Jun 03 '15 at 22:40
  • 1
    One thing I would like to mention here that while `i = (i, ++i, 1) + 1;` certainly doesn't invoke UB, `i += (i, ++i, 1) + 1;` does. – haccks Jun 04 '15 at 07:31
  • That's true, since you do not know which incremention will take place first. But isn't this unspesified behaviour @haccks? – gsamaras Jun 04 '15 at 14:14
  • Well the answer I linked states: "Certain other aspects and operations of the abstract machine are described in this International Standard as unspecified (for example, order of evaluation of arguments to a function). Where possible, this International Standard defines a set of allowable behaviors. These define the nondeterministic aspects of the abstract machine.", so I guess I am wrong. – gsamaras Jun 04 '15 at 16:13
  • 1
    @gsamaras; Yes. You are wrong. Read this [answer](http://stackoverflow.com/a/30627253/2455888) for explanation. – haccks Jun 04 '15 at 16:14
  • 2
    Totally something else: Is it just me or most of our comments got deleted without our intention? The comment before my `possible duplicate` comment, and your comment after it and I think one more. – LyingOnTheSky Jun 05 '15 at 08:00
  • @LyingOnTheSky I thought of the same thing! +1 to your top answer for that. I asked on Meta about it and it turns out that they were flagged for deletion. The question exists no more in Meta. – gsamaras Jun 05 '15 at 08:45
  • 1
    Good point @haccks! As I said in one of my deleted comments: "If I knew about the comma operator, I would have found the other question. I just saw the expression and could not understand what was going on. I mean, how could I search about the comma operator, if I didn't know about it?" – gsamaras Jun 05 '15 at 20:59
  • @MarounMaroun well I tried to upvote your comment but SO wont let me because it's dumb haha, I quadrouple clicked the upvote button (due to lag) which caused it to upvoted, undo, then re-upvote, then undo, and now it wont let me upvote again because they only let you undo twice or something – Albert Renshaw Jan 27 '16 at 05:38
  • 1
    @gsamaras: Not a downvoter but its easy to understand the downvote. You are asking question about obscure cases that should not exist in the real world. No real organization would allowed the above expression to pass through a code review. Because understanding the syntax is so obscure (you need to ask stackoverflow what it means) it effectively makes the code unreadable and thus should not be allowed to exist in code. Which is also why you should treat all your errors as warnings `-WError` and this would stop the code from even compiling. – Martin York Jun 07 '16 at 15:40
  • @LokiAstari thank your for your analytic comment. However SO is full of such questions, but I get the point! ;) – gsamaras Jun 07 '16 at 17:54
  • 1
    @gsamaras: And all such questions will all have people downvoting them for being useless and pointless questions. – Martin York Jun 07 '16 at 20:44
  • OK @LokiAstari thank you, however, if it wasn't for you, how could I know? The question has ~30 downvotes, at least one could have said so! :) – gsamaras Jun 07 '16 at 21:12
  • http://stackoverflow.com/help/privileges `1,000 established user You've been around for a while; see vote counts`. You should be able to see the vote counts on any question. Just click on the score and it will show the up and down counts for any question or answer you like. – Martin York Jun 07 '16 at 21:44
  • @LokiAstari I know I can view that, but when I see a question with a big positive score, I usually do not view the vote counts. :) – gsamaras Jun 07 '16 at 22:30
  • 1
    Some times it is just enough to read from some memory area to trigger something funny. This is called memory mapped IO [link](https://softwareengineering.stackexchange.com/a/349909/316352) or special registers :D – minus one Dec 22 '21 at 17:38
  • @minusone really interesting, thanks for sharing! – gsamaras Dec 23 '21 at 20:43

7 Answers7

256

In the expression (i, ++i, 1), the comma used is the comma operator

the comma operator (represented by the token ,) is a binary operator that evaluates its first operand and discards the result, and then evaluates the second operand and returns this value (and type).

Because it discards its first operand, it is generally only useful where the first operand has desirable side effects. If the side effect to the first operand does not takes place, then the compiler may generate warning about the expression with no effect.

So, in the above expression, the leftmost i will be evaluated and its value will be discarded. Then ++i will be evaluated and will increment i by 1 and again the value of the expression ++i will be discarded, but the side effect to i is permanent. Then 1 will be evaluated and the value of the expression will be 1.

It is equivalent to

i;          // Evaluate i and discard its value. This has no effect.
++i;        // Evaluate i and increment it by 1 and discard the value of expression ++i
i = 1 + 1;  

Note that the above expression is perfectly valid and does not invoke undefined behavior because there is a sequence point between the evaluation of the left and right operands of the comma operator.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • 1
    although the final expression is valid, isn't the second expression ++i an undefined behaviour ? it is evaluated and the value of uninitialized variable is pre-incremented, which is not right? or am i missing something? – Koushik Shetty Jun 04 '15 at 05:59
  • 2
    @Koushik; `i` is initialized with `5`. Look at the declaration statement `int i = 5;`. – haccks Jun 04 '15 at 06:42
  • 1
    oh my bad. Sorry i honestly dint see that. – Koushik Shetty Jun 04 '15 at 06:43
  • There is a mistake here: ++i will increment i then evaluate it, while i++ will evaluate i then increment it. – Quentin Hayot Jun 04 '15 at 14:54
  • 1
    @QuentinHayot; What? Any side effects takes place after the evaluation of expression. In case of `++i`, this expression will be evaluated, `i` will be incremented and this incremented value will be the value of the expression. In case of `i++`, this expression will be evaluated, the old value of `i` will be the value of the expression, `i` will be incremented at any time between the previous and the next sequence point of the expression. – haccks Jun 04 '15 at 16:35
62

Quoting from C11, chapter 6.5.17, Comma operator

The left operand of a comma operator is evaluated as a void expression; there is a sequence point between its evaluation and that of the right operand. Then the right operand is evaluated; the result has its type and value.

So, in your case,

(i, ++i, 1)

is evaluated as

  1. i, gets evaluated as a void expression, value discarded
  2. ++i, gets evaluated as a void expression, value discarded
  3. finally, 1, value returned.

So, the final statement looks like

i = 1 + 1;

and i gets to 2. I guess this answers both of your questions,

  • How i gets a value 2?
  • Why there is a warning message?

Note: FWIW, as there is a sequence point present after the evaluation of the left hand operand, an expression like (i, ++i, 1) won't invoke UB, as one may generally think by mistake.

Mohit Jain
  • 30,259
  • 8
  • 73
  • 100
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • +1 Sourav, since this explains why the intialization of `i` clearly has no effect! However, I do not think it was so obvious for a guy that does not know the comma operator (and I did not know how to search for help, other than asking a question). Pity I got so many downvotes! I'll check the other answers and then decide which to accept. Thanks! Nice top answer btw. – gsamaras Jun 03 '15 at 08:22
  • I feel I have to explain why I accepted haccks answer. I was ready to accept yours, since it's really answers my both questions. However, if you check the comments of my question, you'll see that some people can't see at first glance why this does not invoke UB. haccks answers provide some relevant info. Of course, I have the answer regarding UB linked in my question, but some people may miss that. Hope you agree with my desicion, if not let me know. :) – gsamaras Jun 03 '15 at 09:12
30
i = (i, ++i, 1) + 1;

Let's analyse it step by step.

(i,   // is evaluated but ignored, there are other expressions after comma
++i,  // i is updated but the resulting value is ignored too
1)    // this value is finally used
+ 1   // 1 is added to the previous value 1

So we obtain 2. And the final assignment now:

i = 2;

Whatever was in i before it's overwritten now.

dlask
  • 8,776
  • 1
  • 26
  • 30
  • It would be nice to state that this happens because of the comma operator. +1 for the step by step analysis though! Nice top answer btw. – gsamaras Jun 03 '15 at 08:30
  • I am sorry for insufficient explanation, I have only a note there (*...but ignored, there are...*). I wanted to explain mainly why the `++i` doesn't contribute to the result. – dlask Jun 03 '15 at 08:37
  • now my for loops will be always like `int i = 0; for( ;(++i, i – CoffeDeveloper Jun 04 '15 at 18:36
19

The outcome of

(i, ++i, 1)

is

1

For

(i,++i,1) 

the evaluation happens such that the , operator discards the evaluated value and will retain just the right most value which is 1

So

i = 1 + 1 = 2
Gopi
  • 19,784
  • 4
  • 24
  • 36
14

You'll find some good reading on the wiki page for the Comma operator.

Basically, it

... evaluates its first operand and discards the result, and then evaluates the second operand and returns this value (and type).

This means that

(i, i++, 1)

will, in turn, evaluate i, discard the result, evaluate i++, discard the result, and then evaluate and return 1.

Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402
  • O_O hell, does that syntax is valid in C++, I remember I had few places where I needed that syntax (basically I wrote : `(void)exp; a= exp2;` while I just needed `a = exp, exp2;`) – CoffeDeveloper Jun 04 '15 at 18:32
13

You need to know what the comma operator is doing here:

Your expression:

(i, ++i, 1)

The first expression, i, is evaluated, the second expression, ++i, is evaluated, and the third expression, 1, is returned for the whole expression.

So the result is: i = 1 + 1.

For your bonus question, as you see, the first expression i has no effect at all, so the compiler complains.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
5

Comma has an 'inverse' precedence. This is what you will get from old books and C manuals from IBM (70s/80s). So the last 'command' is what is used in parent expression.

In modern C its use is strange but is very interesting in old C (ANSI):

do { 
    /* bla bla bla, consider conditional flow with several continue's */
} while ( prepAnything(), doSomethingElse(), logic_operation);

While all operations (functions) are called from left to right, only the last expression will be used as a result to conditional 'while'. This prevent handling of 'goto's to keep a unique block of commands to run before condition check.

EDIT: This avoid also a call to a handling function which could take care of all logic at left operands and so return the logical result. Remember that, we had not inline function in the past of C. So, this could avoid a call overhead.

Luciano
  • 2,695
  • 6
  • 38
  • 53
  • Luciano, you got also link to this answer: http://stackoverflow.com/questions/17902992/what-is-the-proper-use-of-the-comma-operator/17903036#17903036. – gsamaras Jun 11 '15 at 13:37
  • In the early 90s before of inline functions, I used it a lot to optimize and keep code organized. – Luciano Jun 11 '15 at 13:53