【问题标题】:What does i = (i, ++i, 1) + 1; do?i = (i, ++i, 1) + 1 是什么?做?
【发布时间】:2015-08-17 07:50:27
【问题描述】:

在阅读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;
                        ^

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

【问题讨论】:

  • 别做怪事,你会没有朋友的:(
  • 警告信息是您第一个问题的答案。
  • @gsamaras:不。结果 value 被丢弃,而不是修改。真正的答案:逗号运算符创建一个序列点。
  • @gsamaras 你不应该关心当你有积极的分数,甚至超过 10 个问题。
  • 注意:优化编译器可能很简单printf("2\n");

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


【解决方案1】:

逗号具有“逆”优先级。这是您将从 IBM(70 年代/80 年代)的旧书和 C 手册中获得的信息。所以最后一个“命令”是父表达式中使用的。

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

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

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

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

【讨论】:

【解决方案2】:

你需要知道逗号操作符在这里做什么:

你的表情:

(i, ++i, 1)

第一个表达式 i 被计算,第二个表达式 ++i 被计算,第三个表达式 1 被返回整个表达式。

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

对于您的附加问题,如您所见,第一个表达式 i 根本没有效果,因此编译器会抱怨。

【讨论】:

    【解决方案3】:

    引自C11,章节6.5.17,Comma operator

    逗号运算符的左操作数被评估为 void 表达式;有一个 其求值与右操作数求值之间的序列点。那么右边 评估操作数;结果有它的类型和值。

    所以,在你的情况下,

    (i, ++i, 1)
    

    被评估为

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

    所以,最后的语句看起来像

    i = 1 + 1;
    

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

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

    注意:FWIW,因为在评估左侧操作数之后存在一个序列点,所以像(i, ++i, 1) 这样的表达式不会调用UB,因为一个可能 em> 一般会误会。

    【讨论】:

    • +1 Sourav,因为这解释了为什么 i 的初始化显然没有效果!但是,我认为对于一个不知道逗号运算符的人来说,这并不是那么明显(而且我不知道如何寻求帮助,除了问一个问题)。可惜我得到了这么多反对票!我会检查其他答案,然后决定接受哪个。谢谢!顺便说一句,不错的最佳答案。
    • 我觉得我必须解释为什么我接受了黑客的回答。我已经准备好接受你的了,因为它确实回答了我的两个问题。但是,如果您检查我的问题的 cmets,您会发现有些人乍一看无法看到为什么这不会调用 UB。 hacks 答案提供了一些相关信息。当然,我的问题中有关于 UB 的答案,但有些人可能会错过。希望你同意我的决定,如果不告诉我。 :)
    【解决方案4】:

    在表达式(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。

    【讨论】:

    • 虽然最后一个表达式有效,但第二个表达式 ++i 不是未定义的行为吗?它被评估并且未初始化变量的值被预先增加,这是不对的?还是我错过了什么?
    • @Koushik; i 初始化为 5。看声明声明int i = 5;.
    • 哦,我的错。对不起,我真的没看到。
    • 这里有一个错误:++i 会先增加 i 然后计算它,而 i++ 会先计算 i 然后再增加它。
    • @QuentinHayot;什么?任何副作用都会在表达式评估之后发生。在++i 的情况下,这个表达式将被计算,i 将被递增,这个递增的值将是表达式的值。在i++ 的情况下,将评估此表达式,i 的旧值将是表达式的值,i 将在表达式的前一个和下一个序列点之间的任何时间递增。
    【解决方案5】:
    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 中的任何内容。

    【讨论】:

    • 很高兴说明这是因为逗号运算符而发生的。为逐步分析+1!顺便说一句,不错的最佳答案。
    • 很抱歉解释不够,我那里只有一个注释(...但忽略了,有...)。我想主要解释为什么++i 对结果没有贡献。
    • 现在我的 for 循环将永远像 int i = 0; for( ;(++i, i&lt;max); )
    【解决方案6】:

    结果

    (i, ++i, 1)
    

    是

    1
    

    对于

    (i,++i,1) 
    

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

    所以

    i = 1 + 1 = 2
    

    【讨论】:

    • 是的,我也想过,但不知道为什么!
    • @gsamaras 因为逗号运算符评估前一个术语但将其丢弃(即不将其用于赋值等)
    【解决方案7】:

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

    基本上是这样

    ... 计算其第一个操作数并丢弃结果,然后计算第二个操作数并返回此值(和类型)。

    这意味着

    (i, i++, 1)
    

    依次评估i,丢弃结果,评估i++,丢弃结果,然后评估并返回1。

    【讨论】:

    • O_O 见鬼,这种语法在 C++ 中是否有效,我记得我很少有地方需要这种语法(基本上我写的是:(void)exp; a= exp2; 而我只需要a = exp, exp2;)
    猜你喜欢
    • 2012-08-25
    • 2022-07-13
    • 1970-01-01
    • 2012-01-13
    • 1970-01-01
    • 2020-05-24
    • 2021-05-27
    • 1970-01-01
    • 2020-05-24
    相关资源
    最近更新 更多