简体   繁体   English

逗号运算符 , 有什么作用?

[英]What does the comma operator , do?

,运算符在 C 中做了什么?

The expression:表达方式:

(expression1,  expression2)

First expression1 is evaluated, then expression2 is evaluated, and the value of expression2 is returned for the whole expression.首先计算表达式 1,然后计算表达式 2,并为整个表达式返回表达式 2 的值。

I've seen used most in while loops:我见过在while循环中使用最多:

string s;
while(read_string(s), s.len() > 5)
{
   //do something
}

It will do the operation, then do a test based on a side-effect.它将执行操作,然后根据副作用进行测试。 The other way would be to do it like this:另一种方法是这样做:

string s;
read_string(s);
while(s.len() > 5)
{
   //do something
   read_string(s);
}

The comma operator will evaluate the left operand, discard the result and then evaluate the right operand and that will be the result.逗号运算符将评估左操作数,丢弃结果,然后评估右操作数,这将是结果。 The idiomatic use as noted in the link is when initializing the variables used in a for loop, and it gives the following example:链接中提到的惯用用法是在初始化for循环中使用的变量时,它给出了以下示例:

void rev(char *s, size_t len)
{
  char *first;
  for ( first = s, s += len - 1; s >= first; --s)
      /*^^^^^^^^^^^^^^^^^^^^^^^*/ 
      putchar(*s);
}

Otherwise there are not many great uses of the comma operator , although it is easy to abuse to generate code that is hard to read and maintain.否则,逗号运算符用途并不多,尽管它很容易被滥用以生成难以阅读和维护的代码。

From the draft C99 standard the grammar is as follows:C99 标准草案中,语法如下:

expression:
  assignment-expression
  expression , assignment-expression

and paragraph 2 says:第2段说:

The left operand of a comma operator is evaluated as a void expression;逗号运算符的左操作数被评估为 void 表达式; there is a sequence point after its evaluation.在其评估之后有一个序列点。 Then the right operand is evaluated;然后对右操作数求值; the result has its type and value.结果有它的类型和值。 97) 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. 97)如果尝试修改逗号运算符的结果或在下一个序列点之后访问它,则行为未定义。

Footnote 97 says:脚注 97说:

A comma operator does not yield an lvalue .逗号运算符不会产生左值

which means you can not assign to the result of the comma operator .这意味着您不能分配给逗号运算符的结果。

It is important to note that the comma operator has the lowest precedence and therefore there are cases where using () can make a big difference, for example:重要的是要注意逗号运算符的优先级最低,因此在某些情况下使用()可以产生很大的不同,例如:

#include <stdio.h>

int main()
{
    int x, y ;

    x = 1, 2 ;
    y = (3,4) ;

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

will have the following output:将有以下输出:

1 4

The comma operator combines the two expressions either side of it into one, evaluating them both in left-to-right order.逗号运算符将其两侧的两个表达式组合为一个,按从左到右的顺序计算它们。 The value of the right-hand side is returned as the value of the whole expression.右侧的值作为整个表达式的值返回。 (expr1, expr2) is like { expr1; expr2; } (expr1, expr2)就像{ expr1; expr2; } { expr1; expr2; } { expr1; expr2; } but you can use the result of expr2 in a function call or assignment. { expr1; expr2; }但您可以在函数调用或赋值中使用expr2的结果。

It is often seen in for loops to initialise or maintain multiple variables like this:for循环中经常看到像这样初始化或维护多个变量:

for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
{
    /* do something with low and high and put new values
       in newlow and newhigh */
}

Apart from this, I've only used it "in anger" in one other case, when wrapping up two operations that should always go together in a macro.除此之外,我只在另一种情况下“愤怒地”使用它,当包装两个应该始终在宏中一起使用的操作时。 We had code that copied various binary values into a byte buffer for sending on a network, and a pointer maintained where we had got up to:我们有将各种二进制值复制到字节缓冲区中以便在网络上发送的代码,并在我们已经到达的位置维护了一个指针:

unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;

*ptr++ = first_byte_value;
*ptr++ = second_byte_value;

send_buff(outbuff, (int)(ptr - outbuff));

Where the values were short s or int s we did this:当值是shortint时,我们这样做:

*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;

Later we read that this was not really valid C, because (short *)ptr is no longer an l-value and can't be incremented, although our compiler at the time didn't mind.后来我们读到这不是真正有效的 C,因为(short *)ptr不再是左值并且不能递增,尽管我们当时的编译器并不介意。 To fix this, we split the expression in two:为了解决这个问题,我们将表达式一分为二:

*(short *)ptr = short_value;
ptr += sizeof(short);

However, this approach relied on all developers remembering to put both statements in all the time.但是,这种方法依赖于所有开发人员记住始终将这两个语句都放入。 We wanted a function where you could pass in the output pointer, the value and and the value's type.我们想要一个函数,您可以在其中传递输出指针、值和值的类型。 This being C, not C++ with templates, we couldn't have a function take an arbitrary type, so we settled on a macro:这是 C,而不是带有模板的 C++,我们不能让函数采用任意类型,所以我们选择了一个宏:

#define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))

By using the comma operator we were able to use this in expressions or as statements as we wished:通过使用逗号运算符,我们可以在表达式中使用它,或者按照我们的意愿作为语句使用它:

if (need_to_output_short)
    ASSIGN_INCR(ptr, short_value, short);

latest_pos = ASSIGN_INCR(ptr, int_value, int);

send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));

I'm not suggesting any of these examples are good style!我并不是说这些例子中的任何一个都是好的风格! Indeed, I seem to remember Steve McConnell's Code Complete advising against even using comma operators in a for loop: for readability and maintainability, the loop should be controlled by only one variable, and the expressions in the for line itself should only contain loop-control code, not other extra bits of initialisation or loop maintenance.事实上,我似乎记得 Steve McConnell 的Code Complete建议不要在for循环中使用逗号运算符:为了可读性和可维护性,循环应该只由一个变量控制, for行中的表达式本身应该只包含循环控制代码,而不是其他额外的初始化或循环维护。

It causes the evaluation of multiple statements, but uses only the last one as a resulting value (rvalue, I think).它会导致对多个语句的评估,但仅使用最后一个作为结果值(我认为是右值)。

So...所以...

int f() { return 7; }
int g() { return 8; }

int x = (printf("assigning x"), f(), g() );

should result in x being set to 8.应该导致 x 设置为 8。

The comma operator does nothing meaningful, it is a 100% superfluous feature. 逗号运算符没有任何意义,它是100%多余的功能。 The main use of it is "people trying to be smart" and therefore use it to (unintentionally) obfuscate readable code. 它的主要用途是“人们试图变得聪明”,因此使用它来(无意中)混淆可读代码。 The main area of use is to obfuscate for loops, for example: 主要用途是混淆for循环,例如:

for(int i=0, count=0; i<x; i++, count++)

Where int i=0, count=0 is actually not the comma operator, but a declaration list (we're already confused here). 其中int i=0, count=0实际上不是逗号运算符,而是声明列表(我们在这里已经混淆了)。 i++, count++ is the comma operator, which evaluates the left operand first and then the right operand. i++, count++是逗号运算符,它首先计算左操作数,然后计算右操作数。 The result of the comma operator is the result of the right operand. 逗号运算符的结果是右操作数的结果。 The result of the left operand is discarded. 左操作数的结果被丢弃。

But the above code could be written in a much more readable way without the comma operator: 但是如果没有逗号运算符,上面的代码可以用更易读的方式编写:

int count = 0;
for(int i=0; i<x; i++) // readable for loop, no nonsense anywhere
{
  ...
  count++;
}

The only real use of the comma operator I have seen, is artificial discussions about sequence points, since the comma operator comes with a sequence point between the evaluation of the left and right operands. 我看到的逗号运算符的唯一实际用途是关于序列点的人工讨论,因为逗号运算符在左右操作数的求值之间带有序列点。

So if you have some undefined behavior code like this: 所以,如果您有一些未定义的行为代码,如下所示:

printf("%d %d", i++, i++);

You can actually turn it into merely unspecified behavior (order of evaluation of function parameters) by writing 实际上,您可以通过编写将其转换为仅未指定的行为(评估函数参数的顺序)

printf("%d %d", (0,i++), (0,i++));

There is now a sequence point between each evaluation of i++ , so at least the program won't risk to crash and burn any longer, even though the order of evaluation of function parameters remains unspecified. 现在每个i++评估之间都有一个序列点,所以至少程序不会有崩溃和烧毁的风险,即使功能参数的评估顺序仍未指定。

Of course nobody would write such code in real applications, it is only useful for language-lawyer discussions about sequence points in the C language. 当然没有人会在实际应用程序中编写这样的代码,它只对语言 - 律师讨论C语言中的序列点有用。

The comma operator is banned by MISRA-C:2004 and MISRA-C:2012 with the rationale that it creates less readable code. 逗号运算符被MISRA-C:2004和MISRA-C:2012禁止,理由是它创建的代码不太可读。

As earlier answers have stated it evaluates all statements but uses the last one as the value of the expression.正如之前的答案所述,它评估所有语句,但使用最后一个作为表达式的值。 Personally I've only found it useful in loop expressions:就我个人而言,我只发现它在循环表达式中有用:

for (tmp=0, i = MAX; i > 0; i--)

The only place I've seen it being useful is when you write a funky loop where you want to do multiple things in one of the expressions (probably the init expression or loop expression. Something like:我看到它有用的唯一地方是当您编写一个时髦的循环时,您想在其中一个表达式中执行多项操作(可能是 init 表达式或循环表达式。例如:

bool arraysAreMirrored(int a1[], int a2[], size_t size)
{
  size_t i1, i2;
  for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
  {
    if(a1[i1] != a2[i2])
    {
      return false;
    }
  }

  return true;
}

Pardon me if there are any syntax errors or if I mixed in anything that's not strict C. I'm not arguing that the , operator is good form, but that's what you could use it for.如果有任何语法错误或者我混合了任何不严格的 C 语言,请原谅我。我并不是说 , 运算符是好的形式,但这就是你可以使用它的目的。 In the case above I'd probably use a while loop instead so the multiple expressions on init and loop would be more obvious.在上面的情况下,我可能会使用while循环,因此 init 和 loop 上的多个表达式会更明显。 (And I'd initialize i1 and i2 inline instead of declaring and then initializing.... blah blah blah.) (而且我会内联初始化 i1 和 i2,而不是先声明然后再初始化......等等等等。)

I'm reviving this simply to address questions from @Rajesh and @JeffMercado which i think are very important since this is one of the top search engine hits.我恢复这个只是为了解决来自@Rajesh 和@JeffMercado 的问题,我认为这些问题非常重要,因为这是热门搜索引擎之一。

Take the following snippet of code for example以下面的代码片段为例

int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);

It will print它会打印

1 5

The i case is handled as explained by most answers. i案例按照大多数答案的解释进行处理。 All expressions are evaluated in left-to-right order but only the last one is assigned to i .所有表达式都按从左到右的顺序计算,但只有最后一个分配给i The result of the ( expression ) is 1`. (表达式) 的结果is 1`。

The j case follows different precedence rules since , has the lowest operator precedence. j case 遵循不同的优先级规则,因为,具有最低的运算符优先级。 Because of those rules, the compiler sees assignment-expression, constant, constant ... .由于这些规则,编译器会看到assignment-expression, constant, constant ... The expressions are again evaluated in left-to-right order and their side-effects stay visible, therefore, j is 5 as a result of j = 5 .表达式再次以从左到右的顺序进行评估,并且它们的副作用保持可见,因此, j5作为j = 5的结果。

Interstingly, int j = 5,4,3,2,1;有趣的是, int j = 5,4,3,2,1; is not allowed by the language spec.语言规范不允许。 An initializer expects an assignment-expression so a direct , operator is not allowed.初始化程序需要一个赋值表达式,因此不允许使用直接,运算符。

Hope this helps.希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM