题
什么是 ,
运算符在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));
值在哪里 short
或 int
我们这样做了:
*((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
. 。结果 (
表达 )is
1`.
这 j
case 遵循不同的优先规则,因为 ,
具有最低的运算符优先级。由于这些规则,编译器会看到 赋值表达式、常量、常量.... 。表达式再次按从左到右的顺序进行计算,并且它们的副作用仍然可见,因此, j
是 5
后果 j = 5
.
有趣的是, int j = 5,4,3,2,1;
语言规范不允许。一个 初始化器 期望一个 赋值表达式 所以直接 ,
不允许操作员。
希望这可以帮助。