Effect of using a comma instead of a semi-colon in C and C++

C++C

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

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;
}

Solution 3 - C++

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;
    ...
}

Solution 4 - C++

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.

Solution 5 - C++

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSmacLView Question on Stackoverflow
Solution 1 - C++FrankView Answer on Stackoverflow
Solution 2 - C++x4uView Answer on Stackoverflow
Solution 3 - C++UncleBensView Answer on Stackoverflow
Solution 4 - C++catwalkView Answer on Stackoverflow
Solution 5 - C++Michael BurrView Answer on Stackoverflow