i = (i, ++i, 1) + 1; 是什么意思?做?

What does i = (i, ++i, 1) + 1; do?

看了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: warning: left-hand operand of comma expression has no effect

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

为什么?不过应该是我第一个问题的答案会自动回答吧

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

1

对于

(i,++i,1) 

评估的发生使得 , 运算符丢弃评估值并保留最右边的值,即 1

所以

i = 1 + 1 = 2

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

the comma operator (represented by the token ,) is a binary operator that evaluates its first operand and discards the result, and then evaluates the second operand and returns this value (and type).

因为它丢弃了它的第一个操作数,所以它通常 仅在第一个操作数具有理想的副作用时才有用。如果第一个操作数的副作用没有发生,那么编译器可能会生成有关表达式无效的警告。

所以,在上面的表达式中,最左边的 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运算符。

您会在 Comma operator 的 wiki 页面上找到一些不错的读物。

基本上是

... evaluates its first operand and discards the result, and then evaluates the second operand and returns this value (and type).

这意味着

(i, i++, 1)

将依次评估 i、丢弃结果、评估 i++、丢弃结果,然后评估 return 1.

引自 C11,章节 6.5.17Comma operator

The left operand of a comma operator is evaluated as a void expression; there is a sequence point between its evaluation and that of the right operand. Then the right operand is evaluated; the result has its type and value.

所以,在你的情况下,

(i, ++i, 1)

被评估为

  1. i,被评估为空表达式,值被丢弃
  2. ++i,被评估为空表达式,值被丢弃
  3. 最后,1,返回值。

所以,最后的陈述看起来像

i = 1 + 1;

i 达到 2。我想这回答了你的两个问题,

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

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

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 现在被覆盖之前的内容。

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

你的表情:

(i, ++i, 1)

对第一个表达式 i 求值,对第二个表达式 ++i 求值,然后为整个表达式返回第三个表达式 1

所以结果是:i = 1 + 1.

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

逗号具有 'inverse' 优先级。这是您将从 IBM(70 年代/80 年代)的旧书和 C 手册中获得的内容。所以最后的 'command' 是父表达式中使用的。

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

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

虽然所有操作(函数)都是从左到右调用的,但只有最后一个表达式将用作条件 'while' 的结果。 这会阻止处理“goto”以在条件检查之前为 运行 保留一个唯一的命令块。

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