【发布时间】:2010-09-08 07:53:33
【问题描述】:
, 运算符在 C 中的作用是什么?
【问题讨论】:
-
正如我在回答中指出的那样,在左操作数的评估之后有一个序列点。这与函数调用中的逗号不同,后者只是语法。
标签: c operators comma-operator
, 运算符在 C 中的作用是什么?
【问题讨论】:
标签: c operators comma-operator
表达式:
(expression1, expression2)
首先计算表达式 1,然后计算表达式 2,并为整个表达式返回表达式 2 的值。
【讨论】:
i 在任何时候都不会有值 5、4、3、2 或 1。它只是 0。除非表达式有副作用,否则它实际上是无用的。
i = b, c; 等价于(i = b), c,因为赋值= 的优先级高于逗号运算符,。逗号运算符的优先级最低。
expression1, expression2;先求值expression1,大概是为了它的副作用(比如调用函数),然后有一个序列点,然后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);
}
【讨论】:
while (read_string(s) && s.len() > 5)。显然,如果read_string 没有返回值(或没有有意义的返回值),那将不起作用。 (编辑:抱歉,没有注意到这篇文章的年龄。)
while (1) 和break; 语句。试图将代码的中断部分强制进入 while 测试或强制进入 do-while 测试,这通常是浪费精力并使代码更难理解。
while(1) 和break,我更喜欢逗号;
逗号运算符将其两侧的两个表达式组合为一个,以从左到右的顺序对它们进行计算。右侧的值作为整个表达式的值返回。
(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 不再是左值并且不能递增,尽管我们当时的编译器并不介意。为了解决这个问题,我们将表达式一分为二:
*(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 的 Code Complete 建议不要在 for 循环中使用逗号运算符:为了可读性和可维护性,循环应该只由一个变量控制,并且for 行本身应该只包含循环控制代码,而不是其他额外的初始化或循环维护。
【讨论】:
comma operator 将计算左操作数,丢弃结果,然后计算右操作数,这就是结果。链接中提到的惯用用法是在初始化for循环中使用的变量时,它给出了以下示例:
void rev(char *s, size_t len)
{
char *first;
for ( first = s, s += len - 1; s >= first; --s)
/*^^^^^^^^^^^^^^^^^^^^^^^*/
putchar(*s);
}
否则,逗号运算符的使用并不多伟大,尽管它很容易被滥用以生成难以阅读和维护的代码。
来自draft C99 standard的语法如下:
expression:
assignment-expression
expression , assignment-expression
和第2段说:
逗号运算符的左操作数被评估为 void 表达式;在其评估之后有一个序列点。然后评估 右操作数;结果有其类型和值。 97) 如果尝试修改逗号运算符的结果或在下一个序列点之后访问它,则行为未定义。
脚注 97 说:
逗号运算符不会产生左值。
这意味着您不能将结果赋值给 逗号运算符。
请务必注意,逗号运算符具有 lowest precedence,因此在某些情况下使用 () 会产生很大的不同,例如:
#include <stdio.h>
int main()
{
int x, y ;
x = 1, 2 ;
y = (3,4) ;
printf( "%d %d\n", x, y ) ;
}
会有以下输出:
1 4
【讨论】:
我恢复这个只是为了解决来自@Rajesh 和@JeffMercado 的问题,我认为这是非常重要的,因为这是热门搜索引擎之一。
以下面sn-p的代码为例
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 的情况遵循不同的优先级规则,因为, 具有最低的运算符优先级。由于这些规则,编译器会看到 assignment-expression、constant、constant ...。表达式再次以从左到右的顺序进行评估,并且它们的副作用保持可见,因此,j 是 5 作为j = 5 的结果。
有趣的是,语言规范不允许int j = 5,4,3,2,1;。 initializer 需要一个 assignment-expression,因此不允许使用直接的 , 运算符。
希望这会有所帮助。
【讨论】: