什么是 , 运算符在C中做什么?

有帮助吗?

解决方案

表达方式:

(expression1,  expression2)

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

其他提示

我见过最常用的是 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);
}

逗号运算符 将评估左操作数,丢弃结果,然后评估右操作数,这将是结果。这 惯用语 如链接中所述,使用是在初始化 a 中使用的变量时 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

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

值在哪里 shortint我们这样做了:

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

我并不是说这些例子都是好的风格!事实上,我似乎记得史蒂夫·麦康奈尔的 代码完成 建议不要在 a 中使用逗号运算符 for 环形:为了可读性和可维护性,循环应仅由一个变量控制,并且循环中的表达式 for 该行本身应该只包含循环控制代码,而不包含其他额外的初始化或循环维护位。

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

所以...

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

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

应导致 x 设置为 8。

逗号运算符没有任何意义,它是一个 100% 多余的功能。它的主要用途是“人们试图变得聪明”,因此用它来(无意中)混淆可读代码。主要用途是混淆 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++, ,因此至少程序不会再面临崩溃和烧毁的风险,即使函数参数的评估顺序仍未指定。

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

MISRA-C:2004 和 MISRA-C:2012 禁止使用逗号运算符,理由是它会创建可读性较差的代码。

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

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 和循环上的多个表达式会更明显。(我会内联初始化 i1 和 i2 而不是声明然后初始化......等等等等等等。)

我恢复这个只是为了解决@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 case 遵循不同的优先规则,因为 , 具有最低的运算符优先级。由于这些规则,编译器会看到 赋值表达式、常量、常量.... 。表达式再次按从左到右的顺序进行计算,并且它们的副作用仍然可见,因此, j5 后果 j = 5.

有趣的是, int j = 5,4,3,2,1; 语言规范不允许。一个 初始化器 期望一个 赋值表达式 所以直接 , 不允许操作员。

希望这可以帮助。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top