逗号操作符是干什么的?

这个操作符在C中做什么?

expression方式:

 (expression1, expression2) 

首先评估expression式1,然后评估expression式2,并为整个expression式返回expression式2的值。

我已经看到while循环中使用最多:

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

它会做手术,然后做一个基于副作用的testing。 另一种方法是这样做:

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

逗号运算符将两个expression式的任意一个合并为一个,并按照从左到右的顺序对它们进行评估。 右边的值作为整个expression式的值返回。 (expr1, expr2)就像{ expr1; expr2; } { expr1; expr2; } { expr1; expr2; }但是你可以在函数调用或赋值中使用expr2的结果。

for循环中经常可以看到像这样初始化或维护多个variables:

 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 */ } 

除此之外,我只是在另外一种情况下“愤怒”地使用它,当两个操作应该总是一起在一个macros中结束的时候。 我们有将各种二进制值复制到字节缓冲区中用于在networking上发送的代码,并保留了一个指针,我们已经到达了:

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

如果数值是short的或者是int我们这样做:

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

后来我们读到这个不是真正有效的C,因为(short *)ptr不再是一个l值,不能被增加,尽pipe我们的编译器当时并不介意。 为了解决这个问题,我们把expression式分成两部分:

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

然而,这种方法依赖于所有的开发人员都记得把这两个陈述在所有的时间。 我们需要一个可以传递输出指针,值和值types的函数。 这是C,而不是C ++模板,我们不能有一个函数采取任意types,所以我们决定了一个macros:

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

通过使用逗号运算符,我们可以在expression式中使用它或按照我们所希望的语句:

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

我不build议这些例子中的任何一个都是很好的风格! 事实上,我似乎记得史蒂夫·麦康奈尔的代码完成build议反对甚至在for循环中使用逗号运算符:为了可读性和可维护性,循环只能由一个variables控制, for循环中的expression式应该只包含循环控制代码,而不是其他额外的初始化或循环维护位。

逗号运算符将评估左操作数,放弃结果,然后评估右操作数,这将是结果。 链接中提到的习惯用法是初始化for循环中使用的variables,并给出以下示例:

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

否则, 逗号运算符的用处不大,尽pipe很容易被滥用以产生难以阅读和维护的代码。

从草案C99标准的语法如下:

 expression: assignment-expression expression , assignment-expression 

第2段说:

逗号运算符左操作数被评估为voidexpression式; 评估后有一个顺序点。 然后评估右操作数; 结果有它的types和价值。 97)如果尝试修改逗号运算符的结果或在下一个序列点之后访问它,则行为是不确定的。

脚注97说:

逗号运算符不产生左值

这意味着您不能分配给逗号运算符的结果。

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

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

将有以下输出:

 1 4 

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

所以…

 int f() { return 7; } int g() { return 8; } int x = (printf("assigning x"), f(), g() ); 

应该导致x被设置为8。

逗号运算符没有意义,它是一个100%多余的function。 它的主要用途是“人们试图变得聪明”,因此使用它(无意)混淆可读代码。 主要的使用领域是混淆循环,例如:

 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++每次评估之间现在都有一个顺序点,所以至less程序不会再有崩溃和烧毁的风险,即使函数参数的评估顺序没有指定。

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

MISRA-C:2004和MISRA-C:2012禁止使用逗号运算符,理由是它创build的代码较less。

正如前面的答案所述,它评估所有的陈述,但使用最后一个作为expression式的值。 就个人而言,我只发现它在循环expression式中有用:

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

我见过的唯一有用的地方是当你写一个时髦的循环,你想在其中一个expression式中做多个事情(可能是initexpression式或循环expression式,例如:

 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中混合使用,我不认为这个操作符是好的forms,但是这就是你可以使用的。 在上面的情况下,我可能会使用一个while循环,所以init和loop上的多个expression式会更明显。 (我会初始化i1和i2内联,而不是声明,然后初始化….等等等等等等。)