73

I've noticed on a number of occasions when refactoring various pieces of C and C++ code that a comma is used rather than a semi-colon to seperate statements. Something like this;

int a = 0, b = 0;
a = 5, b = 5;

Where I would have expected

int a = 0, b = 0;
a = 5; b = 5;

I know that C and C++ allow use of commas to seperate statements (notably loop headers), but what is the difference if any between these two pieces of code? My guess is that the comma has been left in as the result of cut & pasting, but is it a bug and does it effect execution?

Glen
  • 21,816
  • 3
  • 61
  • 76
SmacL
  • 22,555
  • 12
  • 95
  • 149
  • 12
    Worth mentioning that the "comma operator" in the second line is semantically different from the comma that delimits initialization expressions in `int a = 0, b = 0;`. – Mehrdad Afshari Jan 18 '10 at 15:35
  • 2
    There's also this problem with the comma operator in that `int* a, b;` isn't the same as `int* a; int* b;` – JPvdMerwe Jan 18 '10 at 15:45
  • 3
    That's why I always put the pointer/reference modifier adjacent to the variable, not the type. `int *a, b;` – mskfisher Jan 18 '10 at 16:50
  • ITs just bad foo. Don't use it. It leads to code that is hard to read. – Martin York Jan 18 '10 at 17:43
  • mskfisher: If you are working for our competitors, continue using your style and don't listen to Martin, please :o) – MaR Jan 18 '10 at 18:08
  • 1
    @mskfisher: It's why I *never* declare more than one variable in a single statement. Then I can put the modifier with the element type, which is more in line with the concepts in more recent strong(er) typed languages. – Sam Harwell Jan 18 '10 at 18:14
  • Formally, of course, a comma never seperates statements. The grammer just doesn't allow it. It can seperate expressions though. – MSalters Jan 19 '10 at 12:05

5 Answers5

106

It doesn't make a difference in the code you posted. In general, the comma separates expressions just like a semicolon, however, if you take the whole as an expression, then the comma operator means that the expression evaluates to the last argument.

Here's an example:

b = (3, 5);

Will evaluate 3, then 5 and assign the latter to b. So b = 5. Note that the brackets are important here:

b = 3, 5;

Will evaluate b = 3, then 5 and the result of the whole expression is 5, nevertheless b == 3.

The comma operator is especially helpful in for-loops when your iterator code is not a simple i++, but you need to do multiple commands. In that case a semicolon doesn't work well with the for-loop syntax.

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Frank
  • 10,461
  • 2
  • 31
  • 46
  • 1
    +1, good explanation. Just want to add a common pitfall which might be useful if you're dealing with a lot of these: typing `a[j, k]` instead of `a[j][k]` usually leads to unintended results. – laura Jan 18 '10 at 15:39
  • Well, technically the comma is a binary operator, so it has only a left and right expression. But when multiple commas are used at the same level the recursive outcome is actually that it evaluates to the last expression. – x4u Jan 18 '10 at 15:42
  • 1
    This is useful for to run an void-init function (before main) by assigning a bool sometimes: bool s_dummy = (init_my_stuff(), true); – Macke Jan 18 '10 at 16:34
  • @Mike Seymour that's nice to know - I saw in college (7ish years ago) when I was switching to C from pascal - it was pretty horrible to debug. – laura Jan 18 '10 at 18:32
  • @Mike Just to add - a[j,k] compiles well in visual studio 2008 compiler,and as I expected comma operator to evaluate till last argument I got it equal to a[k].But in gcc i got runtime error! - http://ideone.com/vDdl5 – ZoomIn Feb 22 '12 at 05:52
  • @ZoomIn: It looks like I was hallucinating when I wrote that comment - I had another look at the language standards and couldn't find the rule I thought was there. Anyway, your code gives a runtime error because you don't return zero from `main`; and in C++ it won't compile due to the implicit pointer-to-integer conversion (allowed in C, but most compilers will give a warning). – Mike Seymour Feb 22 '12 at 09:51
  • 1
    @MikeSeymour: That `main` is nonstandard anyways because it "returns" `void`, instead of `int`. The C standard allows exactly two forms: `int main(int,char**)` and `int main(void)`. C++ is the same, although some people mistakenly believe that a `void` return type is allowed there. – Tim Čas Apr 14 '14 at 15:31
25

The comma is a operator that returns a value which is always the 2nd (right) argument while a semicolon just ends statements. That allows the comma operator to be used inside other statements or to concatenate multiple statements to appear as one.

Here the function f(x) gets called and then x > y is evaluated for the if statement.

if( y = f(x), x > y )

An example when it's used just to avoid a the need for block

if( ... )
   x = 2, y = 3;

if( ... ) {
   x = 2;
   y = 3;
}
x4u
  • 13,877
  • 6
  • 48
  • 58
9

The comma operator evaluates all operands from left to right, and the result is the value of the last operand.

It is mostly useful in for-loops if you want to do multiple actions in the "increment" part, e.g (reversing a string)

for (int lower = 0, upper = s.size() - 1; lower < upper; ++lower, --upper)
    std::swap(s[lower], s[upper]);

Another example, where it might be an option (finding all occurrences in a string):

#include <string>
#include <iostream>

int main()
{
    std::string s("abracadabra");
    size_t search_position = 0;
    size_t position = 0;

    while (position = s.find('a', search_position), position != std::string::npos) {
        std::cout << position << '\n';
        search_position = position + 1;
    }
}

In particular, logical and cannot be used for this condition, since both zero and non-zero can mean that the character was found in the string. With comma, on the other hand, position = s.find() is called each time when the condition is evaluated, but the result of this part of the condition is just ignored.

Naturally there are other ways to write the loop:

while ((position = s.find('a', search_position)) != std::string::npos)

or just

while (true) {
    position = s.find('a', search_position);
    if (position == std::string::npos)
        break;
    ...
}
UncleBens
  • 40,819
  • 6
  • 57
  • 90
6

One usage would be in code golfing:

if (x == 1) y = 2, z = 3;
if (x == 1) { y = 2; z = 3; }

The first line is shorter, but that looks too confusing to use in regular development.

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
catwalk
  • 6,340
  • 25
  • 16
6

As Frank mentioned, how the comma operator is used in your example doesn't cause a bug. The comma operator can be confusing for several reasons:

  • it's not seen too often because it's only necessary in some special situations
  • there are several other syntactic uses of the comma that may look like a comma operator - but they aren't (the commas used to separate function parameters/arguments, the commas used to separate variable declarations or initializers)

Since it's confusing and often unnecessary, the comma operator should be avoided except for some very specific situations:

  • it can be useful to perform multiple operation in one or more of a for statement's controlling expressions
  • it can be used in preprocessor macros to evaluate more than one expression in a single statement. This is usually done to allow a macros to do more than one thing and still be a a single expression so the macro will 'fit' in places that only allow an expression.

The comma operator is a hackish operator pretty much by definition - it's to hack in 2 things where only one is allowed. It's almost always ugly, but sometimes that's all you've got. And that's the only time you should use it - if you have another option, don't use the comma operator.

Off the top of my head I can't think of too many other reasons to use the operator, since you can get a similar effect by evaluating the expressions in separate statements in most other situations (though I'm sure that someone will comment on a another use that I've overlooked).

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760