逗号运算符如何工作

Posted

技术标签:

【中文标题】逗号运算符如何工作【英文标题】:How does the Comma Operator work 【发布时间】:2010-09-08 10:08:52 【问题描述】:

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

例如,如果我这样做:

a = b, c;  

a 最终等于 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;

【问题讨论】:

在此处了解更多信息。 ***.com/questions/12824378/… What does the comma operator `,` do in C? 的可能重复项。它打败了你一天。而 lillq 的回答提供了关于 a = (b, c); 的问题的答案。 但在这种情况下,a = b, c = d; 实际上与预期的a = b; c = d; 执行相同? @NargothBond 不一定。如果bd 是使用(和修改)公共状态的函数评估,则直到C++17 才定义执行顺序。 【参考方案1】:

请注意逗号运算符可能在 C++ 中被重载。因此,实际行为可能与预期大相径庭。

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

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

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

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

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

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

【讨论】:

嗯,你不能改变优先级,这意味着你应该在你的列表周围加上括号。 @Jeff 相反。用括号括住列表,这将不起作用,因为那时编译器只会看到两个 char[] 之间的逗号运算符,不能重载。代码有意首先调用operator=,然后为每个剩余元素调用operator,【参考方案2】:

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

a = b, c;

相当于:

(a = b), c;

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

a+b, c(), d

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

someFunc(arg1, arg2, arg3)

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

【讨论】:

值得指出,, 的优先级如此之低,甚至落后于 自身 ;) ...即:逗号作为-运算符 i> 的优先级低于逗号作为分隔符。因此,如果您想在单个函数参数、变量赋值或其他逗号分隔列表中使用逗号作为运算符 - 那么您需要使用括号,例如:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;【参考方案3】:

等于b

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

【讨论】:

【参考方案4】:

逗号运算符:

优先级最低 是左关联的

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

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

对于大多数运算符,允许编译器选择执行顺序,甚至在不影响最终结果的情况下甚至需要跳过执行(例如false &amp;&amp; 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&lt;a,b&gt; --- 逗号分隔的模板参数列表 int foo(int a, int b) --- 逗号分隔的参数列表 Foo::Foo() : a(5), b(3) --- 类构造函数中以逗号分隔的初始化列表

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

延伸阅读:http://en.wikipedia.org/wiki/Comma_operator

【讨论】:

值得注意的是,如果operator , 被重载,您将失去任何对关联性的保证(就像您失去operator&amp;&amp;operator|| 的短路属性一样,如果它们被重载) ? 逗号运算符是左关联的,无论它是否重载。表达式a, b, c 总是意味着(a, b), c 而不是a, (b, c)。如果元素的类型不同,后一种解释甚至可能导致编译错误。您可能追求的是参数的评估顺序?我对此不确定,但也许你是对的:c 可能会在 之前 (a, b) 被评估,即使逗号是左关联的。 只是对类构造函数中逗号分隔的初始化列表的一点评论,顺序不是由列表中的位置决定的。顺序由类的声明位置决定。例如。 struct Foo Foo() : a(5), b(3) int b; int a; a(5) 之前评估 b(3)。如果您的列表是这样的,这很重要:Foo() : a(5), b(a) 。 b 不会设置为 5,而是 a 的未初始化值,您的编译器可能会或可能不会警告。 我最近遇到了一个有两个浮点数的逗号运算符,评估和丢弃一个数字有什么意义? 我认为没有人能回答这个问题。您必须在上下文中显示它。可能是一个单独的问题?【参考方案5】:

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

d = (a = b, c);

a 等于 bd 等于 c

【讨论】:

几乎正确。语句没有值,表达式有。该表达式的值为 c。 为什么用这个代替a = b; d = c; 这让我明白了人们在谈论什么副作用。【参考方案6】:

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

【讨论】:

【参考方案7】:

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

#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;

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

【讨论】:

那么第二个示例与仅在该行添加i = 1; 有何不同?【参考方案8】:

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

【讨论】:

【参考方案9】:

第一件事:逗号实际上不是运算符,对于编译器来说,它只是一个标记,在上下文中与其他标记一起获得意义。

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

示例 1:

为了理解不同上下文中相同标记的含义之间的差异,我们看一下这个例子:

class Example 
   Foo<int, char*> ContentA;

通常 C++ 初学者会认为这个表达式可以/可以比较事物,但这是绝对错误的,&lt;&gt;, 标记的含义取决于使用的上下文。

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

示例 2:

当我们编写一个带有多个初始化变量和/或多个表达式的典型 for 循环时,我们也使用逗号:

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

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

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

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

回到问题,代码

a = b, c;

对编译器意味着类似

(a = b), c;

因为=令牌/运算符的priority高于,令牌的优先级。

这在上下文中被解释为

a = b;
c;

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

【讨论】:

当然是,也许我使用了错误的术语(对于词法分析器,它是一个标记,当然) 作为一个与 operator, (原文如此)一起工作的人,逗号确实是一个运算符。 虽然识别给定的逗号标记是否被识别为逗号运算符(与例如参数分隔符相反)本身可能是一个挑战,但这个问题专门关于 逗号运算符.

以上是关于逗号运算符如何工作的主要内容,如果未能解决你的问题,请参考以下文章

逗号运算符如何与 C++ 中的 cout 一起使用?

逗号运算符的三角归约如何知道制作所有列表的列表?

为啥以下代码中逗号运算符的工作方式有所不同?

编译器如何知道函数调用中的逗号不是逗号运算符?

用逗号运算符做 while ( , ) ,这可能吗?

如何将表中列中的逗号分隔值放入 SQL IN 运算符?