我=(i,++ i,1)+ 1;做?

时间:2015-06-03 08:12:29

标签: c operators expression compiler-warnings comma-operator

在阅读this answer关于未定义的行为和序列点之后,我写了一个小程序:

#include <stdio.h>

int main(void) {
  int i = 5;
  i = (i, ++i, 1) + 1;
  printf("%d\n", i);
  return 0;
}

输出为2。天啊,我没有看到减量来了!这里发生了什么?

另外,在编译上面的代码时,我收到了警告:

  

px.c:5:8:警告:逗号表达式的左侧操作数无效

  [-Wunused-value]   i = (i, ++i, 1) + 1;
                        ^

为什么呢?但可能会在第一个问题的答案中自动回答。

7 个答案:

答案 0 :(得分:256)

在表达式(i, ++i, 1)中,使用的逗号是comma operator

  

逗号运算符(由标记,表示)是一个二元运算符,它计算第一个操作数并丢弃结果,然后计算第二个操作数并返回该值(和类型)。

因为它丢弃了它的第一个操作数,所以通常仅在第一个操作数具有所需副作用的情况下才有用。如果没有发生对第一个操作数的副作用,则编译器可能会生成有关表达式的警告而不起作用。

因此,在上面的表达式中,将评估最左边的i,并且将丢弃其值。然后将++i进行评估,并将i增加1,同时表达式++i的值将被丢弃,i的副作用是永久性的。然后将评估1,表达式的值将为1

相当于

i;          // Evaluate i and discard its value. This has no effect.
++i;        // Evaluate i and increment it by 1 and discard the value of expression ++i
i = 1 + 1;  

请注意,上述表达式完全有效且不会调用未定义的行为,因为在逗号运算符的左右操作数的求值之间存在sequence point

答案 1 :(得分:62)

引自C11,章6.5.17Comma operator

  

逗号运算符的左操作数被计算为void表达式;有一个   其评估与右操作数之间的序列点。然后是正确的   操作数被评估;结果有其类型和价值。

所以,在你的情况下,

(i, ++i, 1)

评估为

  1. i,被评估为void表达式,值被丢弃
  2. ++i,被评估为void表达式,值被丢弃
  3. 最后,1,返回值。
  4. 所以,最后的陈述看起来像是

    i = 1 + 1;
    

    i到达2。我想这可以解答你的两个问题,

    • i如何获得值2?
    • 为什么会收到警告信息?

    注意:FWIW,因为在评估左手操作数后存在序列点,像(i, ++i, 1)这样的表达式不会调用UB,作为一个可能通常会误认为。

答案 2 :(得分:30)

i = (i, ++i, 1) + 1;

让我们一步一步地分析它。

(i,   // is evaluated but ignored, there are other expressions after comma
++i,  // i is updated but the resulting value is ignored too
1)    // this value is finally used
+ 1   // 1 is added to the previous value 1

所以我们获得2.现在最后的任务:

i = 2;

i 中的任何内容现在都被覆盖了。

答案 3 :(得分:19)

的结果
(i, ++i, 1)

1

有关

(i,++i,1) 

评估发生时,,运算符会丢弃评估值,并保留最正确的值1

所以

i = 1 + 1 = 2

答案 4 :(得分:14)

你会在Comma operator的维基页面上找到一些好的阅读。

基本上,它

  

...计算其第一个操作数并丢弃结果,然后计算第二个操作数并返回该值(并键入)。

这意味着

(i, i++, 1)
反过来,

会评估i,放弃结果,评估i++,弃掉结果,然后评估并返回1

答案 5 :(得分:13)

您需要知道逗号运算符在这里做了什么:

你的表达:

(i, ++i, 1)

评估第一个表达式i,计算第二个表达式++i,并为整个表达式返回第三个表达式1

结果是:i = 1 + 1

对于你的奖金问题,如你所见,第一个表达式i根本没有效果,所以编译器会抱怨。

答案 6 :(得分:5)

逗号有一个反向的&#39;优先。这是您从IBM(70年代/ 80年代)的旧书和C手册中获得的内容。所以最后一个命令&#39;是父表达式中使用的。

在现代C中,它的使用很奇怪,但在旧的C(ANSI)中非常有趣:

do { 
    /* bla bla bla, consider conditional flow with several continue's */
} while ( prepAnything(), doSomethingElse(), logic_operation);

虽然从左到右调用所有操作(函数),但只有最后一个表达式将被用作条件&#39;而#39;。 这样可以防止处理“goto”以在条件检查之前保留一个唯一的命令块。

编辑:这也避免了对处理函数的调用,该处理函数可以处理左操作数的所有逻辑,因此返回逻辑结果。请记住,我们在C的过去没有内联函数。因此,这可以避免调用开销。