,操作符在c中做什么?
表达式:
1
| (expression1, expression2) |
首先计算表达式1,然后计算表达式2,并为整个表达式返回表达式2的值。
我见过在while循环中使用最多:
1 2 3 4 5
| string s;
while(read_string(s), s.len() > 5)
{
//do something
} |
它将进行操作,然后根据副作用进行测试。另一种方法是这样做:
1 2 3 4 5 6 7
| string s;
read_string(s);
while(s.len() > 5)
{
//do something
read_string(s);
} |
。
逗号运算符将计算左操作数,放弃结果,然后计算右操作数,这就是结果。链接中提到的惯用用法是在初始化for循环中使用的变量时,它给出了以下示例:
1 2 3 4 5 6 7
| void rev(char *s, size_t len)
{
char *first;
for ( first = s, s += len - 1; s >= first; --s)
/*^^^^^^^^^^^^^^^^^^^^^^^*/
putchar(*s);
} |
否则逗号运算符的用处就不多了,尽管生成难以读取和维护的代码很容易被滥用。
根据C99标准草案,语法如下:
1 2 3
| expression:
assignment-expression
expression , assignment-expression |
号
第2段说:
The left operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation. Then the right operand is evaluated; the result has its type and value. 97) If an attempt is made to modify the result of a comma operator or to access it after the next sequence point, the behavior is undefined.
号
脚注97说:
A comma operator does not yield an lvalue.
号
这意味着您不能为逗号运算符的结果赋值。
需要注意的是,逗号运算符的优先级最低,因此在某些情况下,使用()可能会产生很大的差异,例如:
1 2 3 4 5 6 7 8 9 10 11 12
| #include <stdio.h>
int main()
{
int x, y ;
x = 1, 2 ;
y = (3,4) ;
printf("%d %d
", x, y ) ;
} |
将具有以下输出:
。
逗号运算符将它两边的两个表达式组合为一个表达式,按从左到右的顺序对它们进行计算。右侧的值作为整个表达式的值返回。(expr1, expr2)与{ expr1; expr2; }相似,但可以在函数调用或赋值中使用expr2的结果。
它经常出现在for循环中,用于初始化或维护多个变量,如下所示:
1 2 3 4 5
| 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 */
} |
。
除此之外,我只在另一种情况下"愤怒地"使用它,在总结两个应该始终在宏中一起执行的操作时。我们的代码将各种二进制值复制到一个字节缓冲区中,以便在网络上发送,并且在我们到达的地方维护了一个指针:
1 2 3 4 5 6 7
| unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;
*ptr++ = first_byte_value;
*ptr++ = second_byte_value;
send_buff(outbuff, (int)(ptr - outbuff)); |
如果值是shorts或ints,我们这样做:
1 2
| *((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value; |
。
后来我们读到这不是真正有效的c,因为(short *)ptr不再是l值,不能递增,尽管当时我们的编译器并不介意。为了解决这个问题,我们将表达式分为两部分:
1 2
| *(short *)ptr = short_value;
ptr += sizeof(short); |
然而,这种方法依赖于所有开发人员记住始终将这两个语句放在一起。我们需要一个函数,您可以在其中传递输出指针、值和值的类型。这是C,不是C++的模板,我们不能有一个任意类型的函数,所以我们在一个宏上进行了讨论:
1
| #define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type)) |
。
通过使用逗号运算符,我们可以在表达式中或作为我们希望的语句使用它:
1 2 3 4 5 6
| 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行本身的表达式应该只包含循环控制代码,而不是其他额外的初始化位。R回路维护。
它导致对多个语句进行评估,但只使用最后一个语句作为结果值(我认为是右值)。
所以…
1 2 3 4
| int f() { return 7; }
int g() { return 8; }
int x = (printf("assigning x"), f(), g() ); |
号
应导致x设置为8。
逗号运算符没有任何意义,它是一个100%多余的特性。它的主要用途是"人们试图变得聪明",因此使用它(无意中)混淆可读代码。主要的用途是混淆循环,例如:
1
| for(int i=0, count=0; i<x; i++, count++) |
其中int i=0, count=0实际上不是逗号运算符,而是声明列表(这里我们已经混淆了)。i++, count++是逗号运算符,它首先计算左操作数,然后计算右操作数。逗号运算符的结果是右操作数的结果。左操作数的结果将被丢弃。
但是,如果不使用逗号运算符,上述代码可以以更可读的方式编写:
1 2 3 4 5 6
| int count = 0;
for(int i=0; i<x; i++) // readable for loop, no nonsense anywhere
{
...
count++;
} |
。
我看到的逗号运算符的唯一真正用途是关于序列点的人工讨论,因为逗号运算符在左操作数和右操作数的计算之间带有序列点。
因此,如果您有一些未定义的行为代码,比如:
1
| printf("%d %d", i++, i++); |
。
实际上,您可以通过编写
1
| printf("%d %d", (0,i++), (0,i++)); |
现在,在对i++的每个评估之间都有一个序列点,因此至少程序不会再冒崩溃和烧坏的风险,即使函数参数的评估顺序仍未明确。
当然,没有人会在实际的应用程序中编写这样的代码,它只对语言律师讨论C语言中的序列点有用。
逗号运算符被misra-c:2004和misra-c:2012禁止,理由是它创建的代码可读性较低。
正如前面的答案所述,它计算所有语句,但使用最后一个语句作为表达式的值。我个人认为它只在循环表达式中有用:
1
| for (tmp=0, i = MAX; i > 0; i--) |
我唯一看到它有用的地方是编写一个时髦的循环,在这个循环中,您希望在一个表达式(可能是in it表达式或循环表达式)中执行多个操作。比如:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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,而不是声明然后初始化….等等。)
我只是想回答@rajesh和@jeffmercado的问题,我认为这是非常重要的,因为这是搜索引擎的热门话题之一。
以下面的代码片段为例
1 2 3 4 5
| int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d
", i , j); |
。
它将打印
i案件的处理与大多数答案一样。所有表达式都按从左到右的顺序计算,但只有最后一个表达式被分配给i。(表达的结果)is1`。
由于,具有最低的运算符优先级,因此j大小写遵循不同的优先级规则。由于这些规则,编译器可以看到赋值表达式、常量、常量……表达式按从左到右的顺序重新计算,其副作用保持可见,因此,由于j = 5,j是5。
有趣的是,语言规范不允许使用int j = 5,4,3,2,1;。初始值设定项需要赋值表达式,因此不允许使用直接,运算符。
希望这有帮助。