逗号运算符做什么?


167

什么是,经营者用C呢?



1
正如我在回答中所指出的那样,在左操作数求值之后有一个序列点。这与语法调用中的逗号不同。
Shafik Yaghmour 2014年

2
@SergeyK。—鉴于这个问题早在另一个问题被问及回答之前,另一个很有可能是该问题的重复。但是,另一个也同时带有cc ++双重标记,这很麻烦。这是仅C的问答,答案不错。
乔纳森·莱夫勒

Answers:


129

表达方式:

(expression1,  expression2)

首先对expression1求值,然后对expression2求值,然后为整个表达式返回expression2的值。


3
那么如果我写i =(5,4,3,2,1,0),那么理想情况下它应该返回0,对吗?但是我被赋值为5?您能帮我了解我要去哪里吗?
Jayesh

19
@James:逗号运算的值将始终是最后一个表达式的值。任何时候都不i会有值5、4、3、2或1。它只是0。除非表达式有副作用,否则几乎没有用。
Jeff Mercado 2010年

6
请注意,在逗号表达式的LHS评估与RHS评估之间有一个完整的序列点(有关C99标准的引用,请参阅Shafik Yaghmour回答)。这是逗号运算符的重要属性。
Jonathan Leffler 2014年

5
i = b, c;之所以等于是(i = b), c因为因为赋值的=优先级比逗号运算符高,。逗号运算符的优先级最低。
周期学家

1
我担心括号会在两个方面产生误导:(1)不必要-逗号运算符不必用括号括起来;(2)它们可能会与函数调用的参数列表周围的括号混淆—但是参数列表中的逗号不是逗号运算符。但是,修复它并非完全无关紧要。可能是:在以下语句中:expression1, expression2;首先expression1对它的副作用(例如调用函数)expression2进行评估,然后确定一个序列点,然后对其进行评估,然后返回值……
Jonathan Leffler

119

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

21
嘿,太好了!为了解决该问题,我经常不得不循环执行一些非常规的事情。
staticsan

6
如果您执行以下操作,虽然它可能不太晦涩,但可读性更高while (read_string(s) && s.len() > 5)。显然,如果read_string没有返回值(或没有有意义的值),那将是行不通的。(编辑:对不起,没有注意到这篇文章的年龄。)
jamesdlin

11
@staticsan不要害怕使用while (1)同一个break;体内声明。试图将代码的突围部分强加到while测试中或向下执行do-while测试中,这通常是在浪费能量,并使代码更难以理解。
potrzebie 2012年

8
@jamesdlin ...人们仍然读过它。如果您有话要说,那就说吧。论坛存在复活线程的问题,因为线程通常按上次发布的日期进行排序。StackOverflow没有这样的问题。
Dimitar Slavchev

3
@potrzebie我逗号一样的做法不过如此while(1)break;
迈克尔

38

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

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

否则,不会有太多伟大的用途逗号操作符,虽然它很容易被滥用来生成代码是难以阅读和维护。

根据C99标准草案,语法如下:

expression:
  assignment-expression
  expression , assignment-expression

第2款称:

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

脚注97说:

逗号运算符不会产生左值

这意味着您无法分配逗号运算符的结果。

重要的是要注意,逗号运算符的优先级最低,因此在某些情况下使用()可以产生很大的不同,例如:

#include <stdio.h>

int main()
{
    int x, y ;

    x = 1, 2 ;
    y = (3,4) ;

    printf( "%d %d\n", x, y ) ;
}

将具有以下输出:

1 4

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));

值是shorts或ints时,我们这样做:

*((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)的代码完整建议,甚至建议在for循环中使用逗号运算符:为了提高可读性和可维护性,循环应仅由一个变量控制,并且该for行中的表达式本身应仅包含循环控制代码,没有其他额外的初始化或循环维护。


谢谢!这是我对StackOverflow的第一个答案:从那时起,我也许已经知道要重视简洁性:-)。
保罗·史蒂芬森

有时,我会像在这里描述解决方案的演变(如何实现)那样,有点冗长。
Green Diod

8

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

所以...

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

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

应该导致x设置为8。


3

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

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

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,而不是声明然后初始化...。等等等等。)


我想你的意思是i1 = 0,i2 =大小-1
弗兰克斯特

-2

我只是为了解决@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( 表达式is1` 的结果。

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

有趣的int j = 5,4,3,2,1;是,语言规范不允许这样做。一个初始预计的赋值表达式所以直接,操作是不允许的。

希望这可以帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.