逗号运算符是做什么的?

时间:2008-09-09 18:34:10

标签: c operators comma-operator

,运营商在C中做了什么?

9 个答案:

答案 0 :(得分:113)

表达式:

(expression1,  expression2)

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

答案 1 :(得分:106)

我见过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);
}

答案 2 :(得分:31)

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

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

否则逗号运算符 使用次数不多,尽管easy to abuse生成难以阅读和维护的代码。

draft C99 standard语法如下:

expression:
  assignment-expression
  expression , assignment-expression

第2段说:

  

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

脚注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

答案 3 :(得分:28)

逗号运算符将其两侧的两个表达式合并为一个,按从左到右的顺序对它们进行评估。右侧的值作为整个表达式的值返回。  (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));

如果值为short s或int s,我们就这样做了:

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

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

*(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行本身应该只包含循环控制代码,而不是其他额外的初始化或循环维护。

答案 4 :(得分:8)

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

因此...

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

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

应该导致x设置为8。

答案 5 :(得分:5)

逗号运算符没有任何意义,它是100%多余的功能。它的主要用途是&#34;人们试图变得聪明&#34;因此使用它来(无意地)混淆可读代码。主要用途是混淆for循环,例如:

for(int i=0, count=0; i<x; i++, count++)

int i=0, count=0实际上不是逗号运算符,而是声明列表(我们在这里已经混淆了)。 i++, count++是逗号运算符,它首先计算左操作数,然后计算右操作数。逗号运算符的结果是右操作数的结果。左操作数的结果被丢弃。

但是如果没有逗号运算符,上面的代码可以用更易读的方式编写:

int count = 0;
for(int i=0; i<x; i++) // readable for loop, no nonsense anywhere
{
  ...
  count++;
}

我见过的逗号运算符的唯一实际用途是对序列点进行人工讨论,因为逗号运算符在左右操作数的求值之间都有一个序列点。

因此,如果你有一些未定义的行为代码:

printf("%d %d", i++, i++);

您实际上可以通过编写

将其转换为仅未指定的行为(评估函数参数的顺序)
printf("%d %d", (0,i++), (0,i++));

现在每个评估i++之间都有一个序列点,所以至少该程序不会再有崩溃和燃烧的风险,即使功能参数的评估顺序仍未指定。 / p>

当然没有人会在实际应用程序中编写这样的代码,它只对语言 - 律师讨论C语言中的序列点有用。

逗号运算符被MISRA-C:2004和MISRA-C:2012禁止,理由是它创建的代码不太可读。

答案 6 :(得分:3)

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

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

答案 7 :(得分:2)

我看到它唯一有用的地方是当你编写一个时髦的循环,你想在其中一个表达式中做多个事情(可能是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内联而不是声明然后初始化......等等等等。)

答案 8 :(得分:-1)

我只是为了解决@Rajesh和@JeffMercado的问题而重现这一点,我认为这些问题非常重要,因为这是热门搜索引擎之一。

以下面的代码段为例

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( 表达式is 1`的结果。

j的情况遵循不同的优先级规则,因为,的运算符优先级最低。由于这些规则,编译器会看到 assignment-expression,常量,常量... 。再次按从左到右的顺序评估表达式,并且它们的副作用仍然可见,因此,j5的结果,j = 5int j = 5,4,3,2,1;

有趣的是,语言规范不允许使用,初始化器需要 assignment-expression ,因此不允许直接使用<input id="txt_Search" type="text" onchange="Call()" /> <script> function Call() { //your code } </script> 运算符。

希望这会有所帮助。