逗号运算符是做什么的?

,操作符在C语言中做什么?

51438 次浏览

表达式:

(expression1,  expression2)

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

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

所以…

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


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

应该导致x被设置为8。

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

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

我看到它唯一有用的地方是当你写一个古怪的循环,你想在一个表达式(可能是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,而不是声明然后初始化....等等等等。)

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

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

当值为__abc0或__abc1时,我们这样做:

*((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))

通过使用逗号操作符,我们可以在表达式或语句中使用this:

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

逗号操作符将计算左操作数,放弃结果,然后计算右操作数,这将是结果。链接中提到的惯用用法是在初始化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

第二款说:

逗号操作符的左操作数被计算为空表达式;在其求值之后有一个序列点。如果试图修改逗号操作符的结果或在下一个序列点之后访问它,则该行为未定义。

脚注97说:

逗号操作符执行不产生左值

这意味着你不能赋值给逗号操作符的结果。

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

#include <stdio.h>


int main()
{
int x, y ;


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


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

将有以下输出:

1 4

我恢复这个只是为了回答@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情况遵循不同的优先级规则,因为,具有最低的操作符优先级。由于这些规则,编译器会看到赋值表达式,常量,常量…。表达式再次按从左到右的顺序求值,并且它们的副作用仍然可见,因此,j作为j = 5的结果是5

有趣的是,语言规范不允许int j = 5,4,3,2,1;初始化器需要赋值表达式,因此不允许直接使用,操作符。

希望这能有所帮助。