【问题标题】:What does the comma operator , do?逗号运算符 , 有什么作用?
【发布时间】:2010-09-08 07:53:33
【问题描述】:

, 运算符在 C 中的作用是什么?

【问题讨论】:

  • 正如我在回答中指出的那样,在左操作数的评估之后有一个序列点。这与函数调用中的逗号不同,后者只是语法。
  • @SergeyK。 — 鉴于这个问题比另一个早几年被问及回答,另一个更有可能是这个问题的重复。但是,另一个也同时带有cc++ 的双重标记,这很麻烦。这是一个仅限 C 的问答,有不错的答案。

标签: c operators comma-operator


【解决方案1】:

表达式:

(expression1,  expression2)

首先计算表达式 1,然后计算表达式 2,并为整个表达式返回表达式 2 的值。

【讨论】:

  • 那么如果我写 i = (5,4,3,2,1,0) 那么理想情况下它应该返回 0,对吗?但我被分配了 5 的值?你能帮我理解我哪里出错了吗?
  • @James:逗号操作的值将始终是最后一个表达式的值。 i 在任何时候都不会有值 5、4、3、2 或 1。它只是 0。除非表达式有副作用,否则它实际上是无用的。
  • 请注意,在逗号表达式的 LHS 求值和 RHS 求值之间有一个完整的序列点(请参阅 Shafik Yaghmouranswer 以获取 C99 标准的引用) .这是逗号运算符的一个重要属性。
  • i = b, c; 等价于(i = b), c,因为赋值= 的优先级高于逗号运算符,。逗号运算符的优先级最低。
  • 我担心括号在两个方面具有误导性:(1)它们不是必需的——逗号运算符不必被括号包围; (2) 它们可能与函数调用的参数列表周围的括号混淆——但参数列表中的逗号不是逗号运算符。但是,修复它并非完全微不足道。可能:语句中:expression1, expression2;先求值expression1,大概是为了它的副作用(比如调用函数),然后有一个序列点,然后expression2求值并返回值…
【解决方案2】:

它会导致对多个语句的评估,但只使用最后一个作为结果值(我认为是右值)。

所以...

int f() { return 7; }
int g() { return 8; }

int x = (printf("assigning x"), f(), g() );

应该导致 x 设置为 8。

【讨论】:

  • 确实如此。如果省略外部大括号,它将设置为 11。非常有趣,在某些情况下绝对值得一个编译器警告。
【解决方案3】:

正如之前的答案所述,它评估所有语句,但使用最后一个作为表达式的值。我个人只发现它在循环表达式中有用:

for (tmp=0, i = MAX; i > 0; i--)

【讨论】:

    【解决方案4】:

    我看到它唯一有用的地方是当您编写一个时髦的循环时,您想在其中一个表达式(可能是 init 表达式或循环表达式)中执行多项操作。类似于:

    bool arraysAreMirrored(int a1[], int a2[], size_t size)
    {
      size_t i1, i2;
      for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
      {
        if(a1[i1] != a2[i2])
        {
          return false;
        }
      }
    
      return true;
    }
    

    如果有任何语法错误或者我混合了任何不严格的 C 语言,请原谅我。我并不是说 , 运算符是好的形式,但这就是你可以使用它的目的。在上述情况下,我可能会改用while 循环,因此 init 和 loop 上的多个表达式会更加明显。 (而且我会内联初始化 i1 和 i2,而不是先声明然后再初始化......等等等等。)

    【讨论】:

    • 我猜你的意思是 i1=0, i2 = size -1
    【解决方案5】:

    我见过在while循环中使用最多:

    string s;
    while(read_string(s), s.len() > 5)
    {
       //do something
    }
    

    它将执行操作,然后根据副作用进行测试。另一种方法是这样做:

    string s;
    read_string(s);
    while(s.len() > 5)
    {
       //do something
       read_string(s);
    }
    

    【讨论】:

    • 嘿,真漂亮!我经常不得不循环做一些非正统的事情来解决这个问题。
    • 尽管如果您执行以下操作,它可能会不那么晦涩且更具可读性:while (read_string(s) &amp;&amp; s.len() &gt; 5)。显然,如果read_string 没有返回值(或没有有意义的返回值),那将不起作用。 (编辑:抱歉,没有注意到这篇文章的年龄。)
    • @staticsan 不要害怕在正文中使用while (1)break; 语句。试图将代码的中断部分强制进入 while 测试或强制进入 do-while 测试,这通常是浪费精力并使代码更难理解。
    • @jamesdlin ...人们仍在阅读它。如果你有什么有用的话,那就说吧。论坛在复活线程方面存在问题,因为线程通常按上次发布日期排序。 StackOverflow 没有这样的问题。
    • @potrzebie 比起while(1)break,我更喜欢逗号;
    【解决方案6】:

    逗号运算符将其两侧的两个表达式组合为一个,以从左到右的顺序对它们进行计算。右侧的值作为整个表达式的值返回。 (expr1, expr2) 类似于 { expr1; expr2; },但您可以在函数调用或赋值中使用 expr2 的结果。

    for循环中经常看到这样初始化或维护多个变量:

    for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
    {
        /* do something with low and high and put new values
           in newlow and newhigh */
    }
    

    除此之外,我只在另一种情况下“愤怒地”使用它,当包装两个应该始终在宏中一起使用的操作时。我们有将各种二进制值复制到字节缓冲区中以便在网络上发送的代码,并维护了一个指针:

    unsigned char outbuff[BUFFSIZE];
    unsigned char *ptr = outbuff;
    
    *ptr++ = first_byte_value;
    *ptr++ = second_byte_value;
    
    send_buff(outbuff, (int)(ptr - outbuff));
    

    值是shorts 或ints 我们这样做了:

    *((short *)ptr)++ = short_value;
    *((int *)ptr)++ = int_value;
    

    后来我们读到这不是真正有效的 C,因为 (short *)ptr 不再是左值并且不能递增,尽管我们当时的编译器并不介意。为了解决这个问题,我们将表达式一分为二:

    *(short *)ptr = short_value;
    ptr += sizeof(short);
    

    但是,这种方法依赖于所有开发人员始终记得同时放入这两个语句。我们想要一个函数,您可以在其中传递输出指针、值和值的类型。这是 C,而不是带有模板的 C++,我们不能让函数采用任意类型,所以我们选择了宏:

    #define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))
    

    通过使用逗号运算符,我们可以根据需要在表达式或语句中使用它:

    if (need_to_output_short)
        ASSIGN_INCR(ptr, short_value, short);
    
    latest_pos = ASSIGN_INCR(ptr, int_value, int);
    
    send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));
    

    我并不是说这些例子中的任何一个都是好的风格!事实上,我似乎记得 Steve McConnell 的 Code Complete 建议不要在 for 循环中使用逗号运算符:为了可读性和可维护性,循环应该只由一个变量控制,并且for 行本身应该只包含循环控制代码,而不是其他额外的初始化或循环维护。

    【讨论】:

    • 谢谢!这是我在 StackOverflow 上的第一个答案:从那时起,我可能了解到简洁是值得重视的 :-)。
    • 有时我看重一些冗长,就像这里描述解决方案的演变(如何到达那里)一样。
    【解决方案7】:

    comma operator 将计算左操作数,丢弃结果,然后计算右操作数,这就是结果。链接中提到的惯用用法是在初始化for循环中使用的变量时,它给出了以下示例:

    void rev(char *s, size_t len)
    {
      char *first;
      for ( first = s, s += len - 1; s >= first; --s)
          /*^^^^^^^^^^^^^^^^^^^^^^^*/ 
          putchar(*s);
    }
    

    否则,逗号运算符的使用并不多伟大,尽管它很容易被滥用以生成难以阅读和维护的代码。

    来自draft C99 standard的语法如下:

    expression:
      assignment-expression
      expression , assignment-expression
    

    第2段说:

    逗号运算符的左操作数被评估为 void 表达式;在其评估之后有一个序列点。然后评估 右操作数;结果有其类型和值。 97) 如果尝试修改逗号运算符的结果或在下一个序列点之后访问它,则行为未定义。

    脚注 97 说:

    逗号运算符不会产生左值

    这意味着您不能将结果赋值给 逗号运算符

    请务必注意,逗号运算符具有 lowest precedence,因此在某些情况下使用 () 会产生很大的不同,例如:

    #include <stdio.h>
    
    int main()
    {
        int x, y ;
    
        x = 1, 2 ;
        y = (3,4) ;
    
        printf( "%d %d\n", x, y ) ;
    }
    

    会有以下输出:

    1 4
    

    【讨论】:

      【解决方案8】:

      我恢复这个只是为了解决来自@Rajesh 和@JeffMercado 的问题,我认为这是非常重要的,因为这是热门搜索引擎之一。

      以下面sn-p的代码为例

      int i = (5,4,3,2,1);
      int j;
      j = 5,4,3,2,1;
      printf("%d %d\n", i , j);
      

      它会打印出来

      1 5
      

      i 案例的处理方式如大多数答案所述。所有表达式都按从左到右的顺序计算,但只有最后一个分配给i( 表达式)is1`的结果。

      j 的情况遵循不同的优先级规则,因为, 具有最低的运算符优先级。由于这些规则,编译器会看到 assignment-expression、constant、constant ...。表达式再次以从左到右的顺序进行评估,并且它们的副作用保持可见,因此,j5 作为j = 5 的结果。

      有趣的是,语言规范不允许int j = 5,4,3,2,1;initializer 需要一个 assignment-expression,因此不允许使用直接的 , 运算符。

      希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 2010-09-14
        • 2022-06-09
        相关资源
        最近更新 更多