tags:

views:

525

answers:

5

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?

+5  A: 

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
+25  A: 

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.

Frank
+1: for the `evaluates to the last argument.`
jldupont
+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
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
@laura: `a[j, k]` is actually a syntax error - there's a special case that disallows the comma operator here, to prevent this mistake.
Mike Seymour
This is useful for to run an void-init function (before main) by assigning a bool sometimes: bool s_dummy = (init_my_stuff(), true);
Marcus Lindblom
@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
A: 

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

catwalk
...and whoever writes code like that, will soon get what they deserve.
SF.
To emphasize the effect of the comma vs the semicolon, your second line should probably use a semicolon between y and z, as in `{y=2;z=3;}`
mskfisher
@mskfisher: thanks for noticing, I mistyped the second example
catwalk
+2  A: 

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
+1  A: 

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).

Michael Burr