逗号运算符如何工作


175

逗号运算符如何在C ++中工作?

例如,如果我这样做:

a = b, c;  

结局等于b还是c?

(是的,我知道这很容易测试-只是在此处记录文档,以便别人快速找到答案。)

更新: 使用逗号运算符时,这个问题暴露了细微差别。只是记录一下:

a = b, c;    // a is set to the value of b!

a = (b, c);  // a is set to the value of c!

这个问题实际上是受代码输入错误的启发。打算是什么

a = b;
c = d;

转换成

a = b,    //  <-  Note comma typo!
c = d;

在此处了解更多信息。stackoverflow.com/questions/12824378/...
编码醪

1
逗号运算符`,`在C中做什么的可能重复项。败了你一天。lillq的答案提供了有关的问题的答案a = (b, c);
2015年

5
但是在这种情况下,a = b, c = d;实际上执行的功能是否与预期的相同a = b; c = d;
Bondolin

@NargothBond不一定。如果bd是使用(和修改)公共状态的函数求值,则直到时才定义执行顺序C++17
nyronium

Answers:



129

请注意,逗号操作符在C ++中可能会重载。因此,实际行为可能与预期的行为有很大不同。

例如,Boost.Spirit非常巧妙地使用逗号运算符来实现符号表的列表初始化器。因此,它使以下语法成为可能和有意义:

keywords = "and", "or", "not", "xor";

注意,由于运算符的优先级,该代码(故意!)与

(((keywords = "and"), "or"), "not"), "xor";

也就是说,调用的第一个运算符是keywords.operator =("and")返回一个代理对象,在该对象上operator,调用其余的:

keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");

嗯,不过您不能更改优先级,这意味着您应该在列表中加上括号。
杰夫·伯奇斯

18
@Jeff相反。如果列表中带有括号,则此方法将不起作用,因为随后编译器只会看到两个之间的逗号运算符char[],不能重载。该代码有意先调用operator=,然后再调用operator,其余每个元素。
Konrad Rudolph

125

在所有C / C ++运算符中,逗号运算符的优先级最低。因此,它始终是绑定到表达式的最后一个,这意味着:

a = b, c;

等效于:

(a = b), c;

另一个有趣的事实是逗号运算符引入了序列点。这意味着表达式:

a+b, c(), d

保证按顺序评估其三个子表达式(a + bc()d)。如果它们有副作用,这是很重要的。通常,允许编译器以他们认为合适的任何顺序评估子表达式。例如,在函数调用中:

someFunc(arg1, arg2, arg3)

参数可以按任意顺序求值。请注意,函数调用中的逗号不是运算符;它们是分隔符。


15
值得指出的是,具有这样的低优先级,甚至落后于自身 ;)...这就是:逗号原样运营商具有比逗号原样较低的优先级分离。因此,如果要在单个函数参数,变量赋值或其他逗号分隔的列表中使用逗号分隔,则需要使用括号,例如:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
underscore_d

68

逗号运算符:

  • 优先级最低
  • 是左联想的

为所有类型(内置和自定义)定义了默认版本的逗号运算符,它的工作方式如下-给定exprA , exprB

  • exprA 被评估
  • 的结果exprA被忽略
  • exprB 被评估
  • 的结果exprB作为整个表达式的结果返回

对于大多数操作员而言,编译器可以选择执行顺序,甚至在不影响最终结果的情况下甚至要求跳过执行(例如,false && foo()将跳过对的调用foo)。但是,对于逗号运算符不是这种情况,并且上述步骤将始终发生*

实际上,默认的逗号运算符的工作方式与分号几乎相同。区别在于,用分号分隔的两个表达式形成两个单独的语句,而逗号分隔将所有表达式保留为单个表达式。这就是为什么在以下情况下有时使用逗号运算符的原因:

  • C语法只需要一个表达式,而不是一个语句。例如在if( HERE )
  • C语法只需要一个语句,而不是更多,例如在for循环初始化中for ( HERE ; ; )
  • 当您想跳过花括号并保留一条语句时:(if (foo) HERE ;请不要这样做,这真的很丑!)

如果语句不是表达式,则不能用逗号替换分号。例如,这些是不允许的:

  • (foo, if (foo) bar)if不是表达式)
  • int x,int y(变量声明不是表达式)

就您而言,我们有:

  • a=b, c;,等于a=b; c;,假定该a类型不会使逗号运算符超载。
  • a = b, c = d;等效于a=b; c=d;,假设它a的类型不会使逗号运算符超载。

请注意,并非每个逗号实际上都是逗号运算符。一些逗号具有完全不同的含义:

  • int a, b; ---变量声明列表以逗号分隔,但它们不是逗号运算符
  • int a=5, b=3; ---这也是逗号分隔的变量声明列表
  • foo(x,y)---以逗号分隔的参数列表。事实上,x并且y可以在被评估的任何命令!
  • FOO(x,y) ---逗号分隔的宏参数列表
  • foo<a,b> ---以逗号分隔的模板参数列表
  • int foo(int a, int b) ---逗号分隔的参数列表
  • Foo::Foo() : a(5), b(3) {} ---用逗号分隔的类构造函数中的初始化列表

*如果您应用优化,则并非完全正确。如果编译器识别出某些代码对其余代码完全没有影响,它将删除不必要的语句。

进一步阅读:http : //en.wikipedia.org/wiki/Comma_o​​perator


值得注意的是,如果operator ,过载,您将失去对关联性的任何保证(就像您失去的短路特性,operator&&并且operator||如果过载)一样?
YoungJohn '16

逗号运算符是左关联的,无论是否过载。表达a, b, c总是意味着(a, b), c永远不会a, (b, c)。如果元素的类型不同,则后一种解释甚至可能导致编译错误。对参数的求值顺序可能是什么?我不确定,但是也许您是对的:即使逗号是左关联的,c也有可能在评估之前 就发生了(a, b)
CygnusX1 2016年

1
仅对类构造函数中用逗号分隔的初始化列表进行一点注释,顺序取决于列表中的位置。顺序由类的声明位置确定。例如在之前struct Foo { Foo() : a(5), b(3) {} int b; int a; }逃避。如果您的列表如下所示,这一点很重要。b不会设置为5,而是a的未初始化值,您的编译器可能会或可能不会发出警告。b(3)a(5)Foo() : a(5), b(a) {}
Jonathan Gawrych '18

我最近遇到了一个带有两个浮点数的逗号运算符,求值和舍弃数字的意义是什么?
亚伦弗兰克

我认为没有人可以回答。您必须在上下文中显示它。可能是一个单独的问题?
CygnusX1 '18 -10-17

38

的值a将为b,但表达式的值将为c。也就是说,在

d = (a = b, c);

a等于b并且d将等于c


19
几乎正确。语句没有值,表达式没有值。该表达式的值是c。
Leon Timmermans

为什么用它代替a = b; d = c;
亚伦·弗兰克

这使我了解到人们在谈论什么副作用。
鞋带



2

是逗号运算符的优先级比赋值运算符低

#include<stdio.h>
int main()
{
          int i;
          i = (1,2,3);
          printf("i:%d\n",i);
          return 0;
}

输出:i = 3
因为逗号运算符总是返回最右边的值。
对于带赋值运算符的逗号运算符:

 int main()
{
      int i;
      i = 1,2,3;
      printf("i:%d\n",i);
      return 0;
}

Ouput:i = 1
我们知道逗号运算符的优先级比赋值的优先级低...


那么第二个例子与仅仅i = 1;在那条线上有什么不同呢?
亚伦弗兰克

-3

首先,首先要注意的是:逗号实际上不是运算符,对于编译器来说,它只是一个令牌,与其他令牌在上下文中具有含义。

这是什么意思,为什么要打扰?

范例1:

为了理解在不同上下文中相同标记的含义之间的区别,我们看一下以下示例:

class Example {
   Foo<int, char*> ContentA;
}

通常,C ++初学者会认为此表达式可以/将比较事物,但是它绝对是错误的<>和的含义,取决于使用的上下文。

上面示例的正确解释当然是模板的实例化。

范例2:

当我们编写具有多个初始化变量和/或一个以上表达式的典型for循环时,在每次循环迭代后都应完成这些操作,我们也使用逗号:

for(a=5,b=0;a<42;a++,b--)
   ...

逗号的含义取决于使用的上下文,这里是for构造的上下文。

上下文中的逗号实际上是什么意思?

为了使它更加复杂(就像在C ++中一样),逗号运算符本身可以被重载(感谢Konrad Rudolph指出)。

回到问题,《守则》

a = b, c;

对编译器来说意味着

(a = b), c;

因为优先的的=令牌/操作比的优先级更高的,令牌。

这是在上下文中解释的

a = b;
c;

(请注意,解释取决于上下文,在这里它既不是函数/方法调用,也不是模板实例化。)


1
可以,也许我使用了错误的术语(对于词法分析器来说,这是一个令牌,可以肯定)
Quonux

2
当与运算符(原文如此)一起使用时,逗号确实是运算符。
DragonLord

2
尽管认识到给定的逗号令牌是否被识别为逗号运算符(例如,与参数分隔符相反)本身可能是一个挑战,但这个问题专门针对逗号运算符
CygnusX1 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.