Comma operator?! Isn’t that thing just a separator? Nope. It’s occasionally an operator. And it can do this:
int i = (0, 1, 2, 3, 4, 5); |
Know what that does? It evaluates the constants 0 through 4, discards the results, and then evaluates and returns the constant 5, which is assigned to ‘i’. Why is that useful? It’s not. The evaluation of the constants is probably optimized away by most compilers. But, the comma introduces a sequence point, so you can do disgusting things like this, ensuring the order of evaluation:
int i = (a2d_init(), a2d_fetchvalue()); |
I don’t care how expressive you are—don’t ever do that. Just know that you CAN do it and that doing it makes you a bad person.
Other, more kosher, but still questionable uses include loops:
for (i = 0, j = 256; i < 256; i++, j--) { … } |
That is all. I had personally completely forgotten about this infrequently-used operator until I encountered some strange code recently:
#include <stdio.h> struct S0 { unsigned f1 : 1; }; struct S0 s; int main (void) { int x = -3; int y = x >= (0, s.f1); /* Ooh! Here it is! */ printf ("%d\n", y); return 0; } |
It’s a reduced test case for compilers, so no—it’s not supposed to make any sense to you. If you’re curious though, you can read more about it here.
Well, hello there HN, reddit, and DZone. I didn’t realize language pedantry was so universally piquing. If you’re interested in embedded development and crave diversity, note that we’re hiring for positions in San Francisco.
Copyright © 2013
Here is use for the comma operator I saw recently
That’s not good! And even a bit silly, if you’ll forgive me.
It says
if (!dry_run && ((stdout_closed = true), close_stream (stdout) != 0))that’s equivalent to
if (dry_run, stdout_closed, close_stream (stdout))because in C, [expressions evaluating to] integers are automatically converted to boolean where necessary.
Also,
dry_runandstdout_closedare presumably variables (if they were function calls they would be writtendry_run()andstdout_closed()). That means their value is retrieved an then discarded. So, the line is equivalent to:if (close_stream (stdout))In
a, b, ...,y, zonly the side effects of the expressionsa...ymatter, their values do not. Forz, both its side effect and its value matter.that’s equivalent to
if (dry_run, stdout_closed, close_stream (stdout))Completely incorrect. The
&&operator in C is not equivalent to the comma operator. Also, you seem to have misread=as==. The original code is actually equivalent toif (!dry_run && (stdout_closed = true) && close_stream (stdout)) { ... }However, it’s still most likely buggy. The more logical way to write the code would be
if (!dry_run && close_stream (stdout)) { stdout_closed = true; /* NOW stdout is closed! Not before! */ ... } else { /* Error! close_stream() FAILED! We need to do something here. */ }You also missed the point that the && operator behaves in a short circuit manner…
So if dry_run is true the rest of the statement will not even get executed…
Same goes for Javascript. UglifyJS uses it to achieve better minifying.
int i = (a2d_init(), a2d_fetchvalue());Why is this so bad? I personally don't use such construct, but it nicely suggest that before fetching value and assign it to i, one needs to call init() first. Looks ok to me.
I use it all the time in the assignment operator where you return a reference to the object in question.
here is a small example (normally they are a bit more verbose).
class A
{
public:
A& operator=(const A& rhs) { return v = rhs.v, *this; }
private:
int v;
};
That’s just a slightly more convoluted way of writing
A& operator=(const A& rhs) { v = rhs.v; return *this; }Along those lines, this syntax is valid in C and Javascript:
if ((longvariablename=j+10)>k) {
}
meaning
longvariablename=j+10;
if (longvariablename>k) {
}
I use “longvariablename” simply to show that it does have a purpose in Javascript (to shorten), but I think it would be hard to argue any benefit in a compiled language. It is certainly confusing semantically. But shorter.
I had encountered the same exact snippet of code on “Embedded in Academia”.
Yup. That’s where I saw it too & the original post is linked above. It’s a fascinating read.
I confess freely to use and abuse of the comma operator. It is in the language for a reason, and the reason is precisely to do the kinds of things you describe. I regularly do this:
while (token = scanner.next(), token.size())
{
… blah blah blah …
}
It’s neat. It’s elegant. If there is some confusion or I don’t think a reader will see it, I reformat it slightly:
while (token = scanner.next(),
token.size())
{
… blah blah blah …
}
I
Woah, that’s awesome. Can’t believe I didn’t think of it.
…I’m going to steal it now.
For those asking, the various examples are bad because you can do exactly the same thing more readably with two statements. In particular this code:
A& operator=(const A& rhs) { return v = rhs.v, *this; }
is exactly equivalent to:
A& operator=(const A& rhs) { v = rhs.v; return *this; }
What is the point of using the comma operator to combine the two statements into one here? The only case where it is (sometimes) better is where syntactically only one statement is allowed, such as the for-loop example. Another relatively-common variant on this is in nested loops where each item processed in the inner loop also deals with one item in the outer loop:
std::vector x;
std::vector y; // Note – x.size() is an integer multiple of y.size()
for( std::vector::iterator i = x.begin(); i != x.end(); i++) {
for(std::vector::iterator j = y.begin(); j != y.end(), i != y.end; j++, i++) {
// Do something with i, j
}
}
Tom, I think you have a bug
Did you mean this?
std::vector x;
std::vector y; // Note – x.size() is an integer multiple of y.size()
for( std::vector::iterator i = x.begin(); i != x.end(); i++) {
for(std::vector::iterator j = y.begin(); j != y.end() && i != y.end; j++, i++) {
// (Note the loop condition is now an && operator instead of a ,)
// Do something with i, j
}
}
Pingback: The comma operator in C | A rail in the sky
I use it for situations where you need something along these lines, though I don’t enjoy the hit in readability:
x.foo(y) ; // need to do something before the loop
while(y.bar()) {
...
x.foo(y) ; // and for every iteration
}
becomes:
while(x.foo(y), y.bar()) {
...
}
I wrote about it here with a gazillion slightly useful examples (for JavaScript)
http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/
I love it! “disgusting things like this”: made me roar with laughter — it’s amazing how many programmers will find more and more stupid and arcane ways to make code unreadable. Great little article!
Hell what does that do??? I know about the second use of it. The first use of it is a bit intriguing to me.
anyways great article!
I see it pop up in for loops ever now and then; it’s not so bad. Using it in a variable initialization wouldn’t fly here; I don’t dig that.
The comma is one of the few operators in Ansi C (together with &&, || and ?:) for which the temporal order of evaluation of operands is guarranteed (for comma, first the left, then the right one).
Note that order of evaluation is different from operator associativity.
If you want to see how overloading the comma operator in C++ can make code more readable, have a look at the Blitz++ template library for scientific computing. E.g. it is used here to facilitate initializing arrays:
http://www.oonumerics.org/blitz/manual/Frames.html
I use the comma operator. But where is it ?
Pingback: Всем сотрудникам отдела! «
Thanks! I have been looking for how this thing works. If you want something really funky looking check out the Lua source code sometimes.
#define luaL_addchar(B,c) \
((void)((B)->n size || luaL_prepbuffsize((B), 1)), \
((B)->b[(B)->n++] = (c)))
It took a bit for me to figure it out but here is the order it runs in (Note: void is just there to avoid compiler errors)
1: “((B)->n size” if the number of chars is less than the buffer size
2: “||” Most compilers will then skip the rest of a conditional unless the left is false so then
3: “luaL_prepbuffsize((B), 1)” it runs this witch expands the buffer
4: ” ((B)->b[(B)->n++] = (c)” then we run this after the comma.
Your right, I would never use this kind of code within a function. As a define it makes it a quick, efficient putc.