关于c ++:逗号运算符如何工作

关于c ++:逗号运算符如何工作

How does the Comma Operator work

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

例如,如果我这样做:

1
a = b, c;

最终是否等于b或c?

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

更新:此问题在使用逗号运算符时暴露了细微差别。 只是记录下来:

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

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

这个问题实际上是受到代码中的拼写错误的启发。 打算做什么

1
2
a = b;
c = d;

转换成

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

请注意,逗号运算符可能在C ++中过载。因此,实际行为可能与预期的行为非常不同。

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

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

请注意,由于运算符优先级,代码(故意!)相同

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

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

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

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

1
a = b, c;

相当于:

1
(a = b), c;

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

1
a+b, c(), d

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

1
someFunc(arg1, arg2, arg3)

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


它等于b

逗号运算符的优先级低于赋值。


逗号运算符:

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

为所有类型(内置和自定义)定义了默认版本的逗号运算符,它的工作方式如下 - 给定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) ---以逗号分隔的参数列表。实际上,xy可以按任何顺序进行评估!
  • foo(x,y) ---以逗号分隔的宏参数列表
  • foo ---以逗号分隔的模板参数列表
  • int foo(int a, int b) ---以逗号分隔的参数列表
  • Foo::Foo() : a(5), b(3) {} ---类构造函数中以逗号分隔的初始化列表

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

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


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

1
d = (a = b, c);

a等于bd等于c


b的值将被分配给a。
什么都不会发生在c


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

1
2
3
4
5
6
7
8
9
#include<stdio.h>
int main()
{
          int i;
          i = (1,2,3);
          printf("i:%d
"
,i);
          return 0;
}

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

1
2
3
4
5
6
7
8
 int main()
{
      int i;
      i = 1,2,3;
      printf("i:%d
"
,i);
      return 0;
}

输出:i = 1

我们知道逗号运算符的优先级低于赋值.....


a的值将等于b,因为逗号运算符的优先级低于赋值运算符。


首先要做的事情是:逗号实际上不是运算符,对于编译器而言,它只是一个在其他标记的上下文中获得含义的标记。

这是什么意思,为什么要这么麻烦?

例1:

要理解不同上下文中同一令牌的含义之间的区别,我们来看看这个例子:

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

通常,C ++初学者会认为这个表达式可以/会比较事情,但它绝对是错误的,<>,标记的含义取决于使用环境。

对上述示例的正确解释当然是它是模板的实例。

例2:

当我们编写一个带有多个初始化变量的典型for循环和/或应该在循环的每次迭代之后完成的多个表达式时,我们也使用逗号:

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

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

上下文中的逗号实际上意味着什么?

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

回到这个问题,即守则

1
a = b, c;

对编译器来说意味着什么

1
(a = b), c;

因为=标记/运算符的优先级高于,标记的优先级。

这是在上下文中解释的

1
2
a = b;
c;

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


推荐阅读