Uses of C comma operator [duplicate]

2018-12-31 14:56发布

This question already has an answer here:

You see it used in for loop statements, but it's legal syntax anywhere. What uses have you found for it elsewhere, if any?

20条回答
回忆,回不去的记忆
2楼-- · 2018-12-31 15:23

I often use it to run a static initializer function in some cpp files, to avoid lazy initalization problems with classic singletons:

void* s_static_pointer = 0;

void init() {
    configureLib(); 
    s_static_pointer = calculateFancyStuff(x,y,z);
    regptr(s_static_pointer);
}

bool s_init = init(), true; // just run init() before anything else

Foo::Foo() {
  s_static_pointer->doStuff(); // works properly
}
查看更多
大哥的爱人
3楼-- · 2018-12-31 15:26

For me the one really useful case with commas in C is using them to perform something conditionally.

  if (something) dothis(), dothat(), x++;

this is equivalent to

  if (something) { dothis(); dothat(); x++; }

This is not about "typing less", it's just looks very clear sometimes.

Also loops are just like that:

while(true) x++, y += 5;

Of course both can only be useful when the conditional part or executable part of the loop is quite small, two-three operations.

查看更多
人间绝色
4楼-- · 2018-12-31 15:27

From the C standard:

The left operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation. Then the right operand is evaluated; the result has its type and value. (A comma operator does not yield an lvalue.)) 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.

In short it let you specify more than one expression where C expects only one. But in practice it's mostly used in for loops.

Note that:

int a, b, c;

is NOT the comma operator, it's a list of declarators.

查看更多
有味是清欢
5楼-- · 2018-12-31 15:28

I had to use a comma to debug mutex locks to put a message before the lock starts to wait.

I could not but the log message in the body of the derived lock constructor, so I had to put it in the arguments of the base class constructor using : baseclass( ( log( "message" ) , actual_arg )) in the initialization list. Note the extra parenthesis.

Here is an extract of the classes :

class NamedMutex : public boost::timed_mutex
{
public:
    ...

private:
    std::string name_ ;
};

void log( NamedMutex & ref__ , std::string const& name__ )
{
    LOG( name__ << " waits for " << ref__.name_ );
}

class NamedUniqueLock : public boost::unique_lock< NamedMutex >
{
public:

    NamedUniqueLock::NamedUniqueLock(
        NamedMutex & ref__ ,
        std::string const& name__ ,
        size_t const& nbmilliseconds )
    :
        boost::unique_lock< NamedMutex >( ( log( ref__ , name__ ) , ref__ ) ,
            boost::get_system_time() + boost::posix_time::milliseconds( nbmilliseconds ) ),
            ref_( ref__ ),
            name_( name__ )
    {
    }

  ....

};
查看更多
看风景的人
6楼-- · 2018-12-31 15:31

In general I avoid using the comma operator because it just makes code less readable. In almost all cases, it would be simpler and clearer to just make two statements. Like:

foo=bar*2, plugh=hoo+7;

offers no clear advantage over:

foo=bar*2;
plugh=hoo+7;

The one place besides loops where I have used it it in if/else constructs, like:

if (a==1)
... do something ...
else if (function_with_side_effects_including_setting_b(), b==2)
... do something that relies on the side effects ...

You could put the function before the IF, but if the function takes a long time to run, you might want to avoid doing it if it's not necessary, and if the function should not be done unless a!=1, then that's not an option. The alternative is to nest the IF's an extra layer. That's actually what I usually do because the above code is a little cryptic. But I've done it the comma way now and then because nesting is also cryptic.

查看更多
只靠听说
7楼-- · 2018-12-31 15:32

qemu has some code that uses the comma operator within the conditional portion of a for loop (see QTAILQ_FOREACH_SAFE in qemu-queue.h). What they did boils down to the following:

#include <stdio.h>

int main( int argc, char* argv[] ){
  int x = 0, y = 0;

  for( x = 0; x < 3 && (y = x+1,1); x = y ){
    printf( "%d, %d\n", x, y );
  }

  printf( "\n%d, %d\n\n", x, y );

  for( x = 0, y = x+1; x < 3; x = y, y = x+1 ){
    printf( "%d, %d\n", x, y );
  }

  printf( "\n%d, %d\n", x, y );
  return 0;
}

... with the following output:

0, 1
1, 2
2, 3

3, 3

0, 1
1, 2
2, 3

3, 4

The first version of this loop has the following effects:

  • It avoids doing two assignments, so the chances of the code getting out of sync is reduced
  • Since it uses &&, the assignment is not evaluated after the last iteration
  • Since the assignment isn't evaluated, it won't try to de-reference the next element in the queue when it's at the end (in qemu's code, not the code above).
  • Inside the loop, you have access to the current and next element
查看更多
登录 后发表回答