for循环的初始化和增量部分中的逗号如何工作?

use*_*519 12 c c++ for-loop comma-operator

我遇到了一个代码如下的for循环:

for ( argc--, argv++; argc > 0; argc--, argv++ )
Run Code Online (Sandbox Code Playgroud)

它是如何工作的?通常for循环看起来像这样:

for (initialization; condition; increment) {/*body of the loop*/}
Run Code Online (Sandbox Code Playgroud)

但是这不包含任何逗号 - 逗号的含义是什么?

Vla*_*cow 26

在C标准(6.8.5.3 for语句)中,for语句以下列形式表示

for ( clause-1 ; expression-2 ; expression-3 ) statement
Run Code Online (Sandbox Code Playgroud)

根据第1条,有书面说明

如果clause-1是表达式,则在第一次控制表达式求值之前将其计算为void表达式

在这个for语句中

for ( argc--, argv++; argc > 0; argc--, argv++ )  
Run Code Online (Sandbox Code Playgroud)

clause-1是argc--, argv++基于逗号运算符的表达式.来自C标准(6.5.17逗号运算符)

2逗号运算符的左操作数被计算为void表达式; 它的评估与右操作数之间存在一个序列点.然后评估右操作数; 结果有它的类型和价值.

唯一的特点是运算符的结果不用于for语句.该表达用于其副作用.

通常,传递给正在运行的程序的第一个参数是它的名称.第1节中的表达式跳过了第一个参数.

比较这两个程序的输出.我们假设用户指定了命令行参数

first second third
Run Code Online (Sandbox Code Playgroud)

该程序的程序输出

#include <stdio.h>

int main( int argc, char * argv[] )
{
    for ( argc--, argv++; argc > 0; argc--, argv++ )
    {
        puts( *argv );
    }        

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

是

first 
second 
third
Run Code Online (Sandbox Code Playgroud)

当子句-1为空(既不是表达式也不是声明)时,该程序的程序输出

#include <stdio.h>

int main( int argc, char * argv[] )
{
    for ( /*argc--, argv++*/; argc > 0; argc--, argv++ )
    {
        puts( *argv );
    }        

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

是

./prog.exe
first 
second 
third
Run Code Online (Sandbox Code Playgroud)

为了使逗号运算符清晰,可以将程序视为第一个演示程序,而不是for循环使用while循环.

#include <stdio.h>

int main( int argc, char * argv[] )
{
    while ( argv++, --argc > 0 )
    {
        puts( *argv );
    }        

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出将与第一个示范程序中的输出相同

first 
second 
third
Run Code Online (Sandbox Code Playgroud)

这里是while语句,也使用了逗号运算符.区别在于,在这种情况下,逗号运算符的值用作条件的值.

注意表达式-3本身也用逗号运算符表示一个表达式.

此外,当问题用C++标记标记时,您应该知道在C++中,for语句的第二个子句(在C++中它被命名为condition)也可以是表达式或声明.

  • "*表达式用于其副作用.*" < - 这是在`子句 - 1`和`for`的`表达-3`典型,因此I**认为良好的答案还应该提到引入的序列点,即使OPs代码无关紧要...... (2认同)

小智 8

正如许多答案中所述,这是逗号运算符,所以

argc--, argv++
Run Code Online (Sandbox Code Playgroud)

只是一个表达.

逗号运算符评估两侧,先左,然后右.结果是右侧的结果.所以你可以写一些奇怪的东西,比如

int a = (x += 5, x + 2);
Run Code Online (Sandbox Code Playgroud)

这将分配结果前加5至x x + 2到a.这样的代码令人困惑,应该避免.但它演示了逗号运算符的一个重要属性:

它充当一个序列点:使用上面的代码,您可以保证在评估之前已经将5添加到x(x的值确实已更改)x + 2.

逗号运算符的主要合理用法是您的问题中显示的用法.它可以在更复杂的for循环中使用,例如具有多种副作用和有保证的顺序.

为了澄清为什么测序可能很重要(它不在你的例子中,因为副作用不相互依赖),请看这个(人为的)例子:

int i, j;
for (i = j = 0; i < 10; ++i, j+=i)
{
    printf("%d\n", j);
}
Run Code Online (Sandbox Code Playgroud)

如果逗号运算符未在此处引入序列点,则您将不知道是j+=i添加递增的i还是不递增的序列点.


hac*_*cks 3

在此for循环中,第一个和最后一个表达式中使用了逗号运算符。所以for声明就像

for(
    (argc--, argv++);  // Expression 1
    argc > 0;          // Expression 2
    (argc--, argv++)   // Expression 3
  )  
Run Code Online (Sandbox Code Playgroud)

只有三个表达式(argc--, argv++), argc > 0并且(argc--, argv++)。
表达式1不一定是声明语句,它可以是任何有效的表达式,甚至可以省略

for(;expression2; expression3)
Run Code Online (Sandbox Code Playgroud)

或者所有表达式都可以省略

for(;;)  
Run Code Online (Sandbox Code Playgroud)

在给定的 for 循环中,(argc--, argv++)用作更新变量的第一个表达式argc,并且argv(argc将减 1,指针argv将加 1)。一旦这些变量的副作用完成,程序将在检查argc > 0for后进入循环体true。这就是当你这样做时会发生的事情

for( i = 1; i < 10; i++)
Run Code Online (Sandbox Code Playgroud)

i = 1更新i为1,然后检查条件。的更新i仅完成一次,然后由表达式更新其余部分i++。