225

What does the, operator do in C?

Lundin's user avatar
Lundin
220k47 gold badges281 silver badges446 bronze badges
askedSep 9, 2008 at 18:34
lillq's user avatar
5
  • possible duplicate ofWhat is the proper use of the comma operator?CommentedAug 28, 2013 at 8:00
  • 1
    As I note in my answer, there is a sequence point after the evaluation of the left operand. This is unlike the comma in a function call which is just grammatical.CommentedJun 25, 2014 at 11:46
  • 3
    @SergeyK. — Given that this was asked and answered years before the other, it is more likely that the other is a duplicate of this question. However, the other is also dual-tagged with bothc andc++, which is a nuisance. This is a C-only Q&A, with decent answers.CommentedMay 18, 2019 at 0:18
  • Does this answer your question?How does the comma operator work, and what precedence does it have?CommentedSep 27, 2023 at 19:21
  • @JL00001: The question you cite is taggedc++ and specifically asks about the rules for C++. It was also asked a day after this question, so if it was tagged forc (which it isn't), it might be a duplicate of this question, rather than vice versa. However, as (at least) one of the answers notes, the comma operator in C++ can be overloaded by a class, to the confusion of all and sundry. That isn't a problem in C — operators cannot be overloaded. So this question is not a duplicate of the other, and the other is not a duplicate of this question.CommentedApr 24, 2024 at 20:39

9 Answers9

192

The expression:

(expression1,  expression2)

Firstexpression1 is evaluated, thenexpression2 is evaluated, and the value ofexpression2 is returned for the whole expression.

aeronaut's user avatar
aeronaut
531 silver badge7 bronze badges
answeredSep 9, 2008 at 18:37
lillq's user avatar
Sign up to request clarification or add additional context in comments.

6 Comments

then if I write i = (5,4,3,2,1,0) then ideally it should return 0, correct? but i is being assigned a value of 5? Can you please help me understand where am I going wrong?
@James: The value of a comma operation will always be the value of the last expression. At no point willi have the values 5, 4, 3, 2 or 1. It is simply 0. It's practically useless unless the expressions have side effects.
Note that there is a full sequence point between the evaluation of the LHS of the comma expression and the evaluation of the RHS (seeShafik Yaghmour'sanswer for a quote from the C99 standard). This is an important property of the comma operator.
i = b, c; is equivalent to(i = b), c because because assignment= has higher precedence than the comma operator,. The comma operator has the lowest precedence of all.
I worry that the parentheses are misleading on two counts: (1) they're not necessary — the comma operator doesn't have to be surrounded by parentheses; and (2) they could be confused with the parentheses around the argument list of a function call — but the comma in the argument list is not the comma operator. However, fixing it is not entirely trivial. Maybe:In the statement:expression1, expression2; firstexpression1 is evaluated, presumably for its side-effects (such as calling a function), then there is a sequence point, thenexpression2 is evaluated and the value returned…
|
167

I've seen used most inwhile loops:

string s;while(read_string(s), s.len() > 5){   //do something}

It will do the operation, then do a test based on a side-effect. The other way would be to do it like this:

string s;read_string(s);while(s.len() > 5){   //do something   read_string(s);}
phuclv's user avatar
phuclv
43.2k16 gold badges191 silver badges556 bronze badges
answeredSep 9, 2008 at 18:56
crashmstr's user avatar

13 Comments

Hey, that's nifty! I've often had to do unorthodox things in a loop to fix that problem.
Although it'd probably be less obscure and more readable if you did something like:while (read_string(s) && s.len() > 5). Obviously that wouldn't work ifread_string doesn't have a return value (or doesn't have a meaningful one). (Edit: Sorry, didn't notice how old this post was.)
@staticsan Don't be afraid to usewhile (1) with abreak; statement in the body. Trying to force the break-out part of the code up into the while test or down into the do-while test, is often a waste of energy and makes the code harder to understand.
@jamesdlin ... and people still read it. If you have something useful to say, then say it. Forums have problems with resurrected threads because threads are usually sorted by date of last post. StackOverflow doesn't have such problems.
@potrzebie I like the comma approach much better thanwhile(1) andbreak;
|
54

Thecomma operator will evaluate the left operand, discard the result and then evaluate the right operand and that will be the result. Theidiomatic use as noted in the link is when initializing the variables used in afor loop, and it gives the following example:

void rev(char *s, size_t len){  char *first;  for ( first = s, s += len - 1; s >= first; --s)      /*^^^^^^^^^^^^^^^^^^^^^^^*/       putchar(*s);}

Otherwise there are not manygreat uses of thecomma operator, although it is easy to abuse to generate code that is hard to read and maintain.

From thedraft C99 standard the grammar is as follows:

expression:  assignment-expression  expression , assignment-expression

andparagraph 2 says:

Theleft operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation. Then theright operand is evaluated; the result has its type and value.97) If an attempt is made to modify the result of a comma operator or to access it after the next sequence point, the behavior is undefined.

Footnote 97 says:

A comma operator doesnot yield an lvalue.

which means you can not assign to the result of thecomma operator.

It is important to note that the comma operator has thelowest precedence and therefore there are cases where using() can make a big difference, for example:

#include <stdio.h>int main(){    int x, y ;    x = 1, 2 ;    y = (3,4) ;    printf( "%d %d\n", x, y ) ;}

will have the following output:

1 4
answeredAug 26, 2013 at 12:29
Shafik Yaghmour's user avatar

Comments

31

The comma operator combines the two expressions either side of it into one, evaluating them both in left-to-right order. The value of the right-hand side is returned as the value of the whole expression.(expr1, expr2) is like{ expr1; expr2; } but you can use the result ofexpr2 in a function call or assignment.

It is often seen infor loops to initialise or maintain multiple variables like this:

for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh){    /* do something with low and high and put new values       in newlow and newhigh */}

Apart from this, I've only used it "in anger" in one other case, when wrapping up two operations that should always go together in a macro. We had code that copied various binary values into a byte buffer for sending on a network, and a pointer maintained where we had got up to:

unsigned char outbuff[BUFFSIZE];unsigned char *ptr = outbuff;*ptr++ = first_byte_value;*ptr++ = second_byte_value;send_buff(outbuff, (int)(ptr - outbuff));

Where the values wereshorts orints we did this:

*((short *)ptr)++ = short_value;*((int *)ptr)++ = int_value;

Later we read that this was not really valid C, because(short *)ptr is no longer an l-value and can't be incremented, although our compiler at the time didn't mind. To fix this, we split the expression in two:

*(short *)ptr = short_value;ptr += sizeof(short);

However, this approach relied on all developers remembering to put both statements in all the time. We wanted a function where you could pass in the output pointer, the value and and the value's type. This being C, not C++ with templates, we couldn't have a function take an arbitrary type, so we settled on a macro:

#define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))

By using the comma operator we were able to use this in expressions or as statements as we wished:

if (need_to_output_short)    ASSIGN_INCR(ptr, short_value, short);latest_pos = ASSIGN_INCR(ptr, int_value, int);send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));

I'm not suggesting any of these examples are good style! Indeed, I seem to remember Steve McConnell'sCode Complete advising against even using comma operators in afor loop: for readability and maintainability, the loop should be controlled by only one variable, and the expressions in thefor line itself should only contain loop-control code, not other extra bits of initialisation or loop maintenance.

Luis's user avatar
Luis
1,3232 gold badges12 silver badges26 bronze badges
answeredSep 11, 2008 at 20:42
Paul Stephenson's user avatar

Comments

8

It causes the evaluation of multiple statements, but uses only the last one as a resulting value (rvalue, I think).

So...

int f() { return 7; }int g() { return 8; }int x = (printf("assigning x"), f(), g() );

should result in x being set to 8.

answeredSep 9, 2008 at 18:37
Ben Collins's user avatar

1 Comment

It does. And it is set to 11 if you leave out the outer braces. Quite interesting and definitely worth a compiler warning for some cases.
4

As earlier answers have stated it evaluates all statements but uses the last one as the value of the expression. Personally I've only found it useful in loop expressions:

for (tmp=0, i = MAX; i > 0; i--)
answeredSep 9, 2008 at 18:43
DGentry's user avatar

Comments

3

Another use - complementary to the already mentioned - is a method to avoid braces in a single line if.

// this:if (anything) A = 1, B = 2;// or:if (anything)  A = 1, B = 2;// instead of:if (anything) { A = 1; B = 2; }// or:if (anything) {  A = 1; B = 2;}
answeredFeb 14, 2024 at 11:05
Petit Bateau's user avatar

Comments

2

The only place I've seen it being useful is when you write a funky loop where you want to do multiple things in one of the expressions (probably the init expression or loop expression. Something like:

bool arraysAreMirrored(int a1[], int a2[], size_t size){  size_t i1, i2;  for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)  {    if(a1[i1] != a2[i2])    {      return false;    }  }  return true;}

Pardon me if there are any syntax errors or if I mixed in anything that's not strict C. I'm not arguing that the , operator is good form, but that's what you could use it for. In the case above I'd probably use awhile loop instead so the multiple expressions on init and loop would be more obvious. (And I'd initialize i1 and i2 inline instead of declaring and then initializing.... blah blah blah.)

answeredSep 9, 2008 at 18:50
Owen's user avatar

1 Comment

I presume you mean i1=0, i2 = size -1
-1

I'm reviving this simply to address questions from @Rajesh and @JeffMercado which i think are very important since this is one of the top search engine hits.

Take the following snippet of code for example

int i = (5,4,3,2,1);int j;j = 5,4,3,2,1;printf("%d %d\n", i , j);

It will print

1 5

Thei case is handled as explained by most answers. All expressions are evaluated in left-to-right order but only the last one is assigned toi. The result of the(expression )is1`.

Thej case follows different precedence rules since, has the lowest operator precedence. Because of those rules, the compiler seesassignment-expression, constant, constant .... The expressions are again evaluated in left-to-right order and their side-effects stay visible, therefore,j is5 as a result ofj = 5.

Interstingly,int j = 5,4,3,2,1; is not allowed by the language spec. Aninitializer expects anassignment-expression so a direct, operator is not allowed.

Hope this helps.

answeredJan 4, 2019 at 11:51
ViNi89's user avatar

Comments

Protected question. To answer this question, you need to have at least 10 reputation on this site (not counting theassociation bonus). The reputation requirement helps protect this question from spam and non-answer activity.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.