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

Posted

技术标签:

【中文标题】for循环的初始化和增量部分中的逗号如何工作?【英文标题】:How do commas in the initialization and increment parts of a for-loop work? 【发布时间】:2018-01-25 19:23:05 【问题描述】:

我在代码中遇到了一个如下所示的 for 循环:

for ( argc--, argv++; argc > 0; argc--, argv++ )

它是如何工作的?通常for 循环如下所示:

for (initialization; condition; increment) /*body of the loop*/

但这不包含任何逗号 - 逗号是什么意思和作用?

【问题讨论】:

它仍然是一样的,只是使用, 运算符(阅读它)并且它只执行第一次递增和递减一次。可能看起来像是循环的一部分,但不是。还有什么不清楚的吗? 为什么初始化部分看起来不像?这是argc--, argv++。这是常规的 C/C++ 表达式。 initialization 可以是任何表达式。它实际上不需要初始化任何东西。你可以把printf("Hello!") 放在那里。 conditionupdation 也是如此。 for(int i=1; i<argc; i++) do_something(argv[i]); 的写法不称职。不幸的是,许多 C 程序员都是装腔作势的人,他们喜欢以最复杂、最模糊的方式编写代码。虽然有能力的程序员会认识到好的代码等于简单易读的代码。 【参考方案1】:

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

for ( clause-1 ; expression-2 ; expression-3 ) statement

根据 clause-1 有这样写的

如果 Clause-1 是一个表达式,它被评估为一个 void 表达式 在控制表达式的第一次评估之前

在这个for语句中

for ( argc--, argv++; argc > 0; argc--, argv++ )  

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

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

唯一的特点是for语句中没有使用运算符的结果。该表达式用于其副作用。

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

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

first second third

本程序的程序输出

#include <stdio.h>

int main( int argc, char * argv[] )

    for ( argc--, argv++; argc > 0; argc--, argv++ )
    
        puts( *argv );
            

    return 0;

first 
second 
third

以及当clause-1为空时该程序的程序输出(既不是表达式也不是声明)

#include <stdio.h>

int main( int argc, char * argv[] )

    for ( /*argc--, argv++*/; argc > 0; argc--, argv++ )
    
        puts( *argv );
            

    return 0;

./prog.exe
first 
second 
third

为了使逗号操作符更清晰,可以考虑一个程序作为第一个演示程序,其中使用了一个 while 循环而不是 for 循环。

#include <stdio.h>

int main( int argc, char * argv[] )

    while ( argv++, --argc > 0 )
    
        puts( *argv );
            

    return 0;

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

first 
second 
third

这里在while 语句中也使用了逗号操作符。不同的是,在这种情况下,逗号运算符的值被用作条件的值。

注意expression-3本身也代表一个带逗号的表达式。

同样问题是用 C++ 标签标记的,那么你应该知道在 C++ 中 for 语句的第二个子句(在 C++ 中被命名为 condition)也可以是表达式或声明。

【讨论】:

"表达式用于其副作用。" for 的clause-1expression-3 中很典型,因此我认为 一个好的答案还应该提到引入的序列点,即使在 OPs 代码中无关...... 当然,在这种特殊情况下,while 循环更短且可读性更强……【参考方案2】:

正如许多答案中已经说明的那样,这是逗号运算符,所以

argc--, argv++

只是一个表达式。

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

int a = (x += 5, x + 2);

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

它作为一个序列点:使用上面的代码,你可以保证 5 已经被添加到 x (x 的值确实改变了),在评估 x + 2 之前。

逗号运算符的主要合理用法是您的问题中显示的那个。它在更复杂的for 循环中派上用场,例如多种副作用和有保证的顺序。

为了阐明为什么排序可能很重要(在你的例子中没有,因为副作用不相互依赖),看看这个(人造的)例子:

int i, j;
for (i = j = 0; i < 10; ++i, j+=i)

    printf("%d\n", j);

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

【讨论】:

【参考方案3】:

对于多次初始化和多次更新/增量,我们使用comma operator(,)。 我们用comma(,)分隔每个实例。 在这种情况下,当进入for循环时,初始化部分的argc--argv++表达式都会被执行。 从那时起,每次循环迭代时,增量部分中的argc--argv++ 表达式都会被执行。

【讨论】:

【参考方案4】:

在这个for 循环中,comma operator 用于第一个和最后一个表达式。所以for 语句就像

for(
    (argc--, argv++);  // Expression 1
    argc > 0;          // Expression 2
    (argc--, argv++)   // Expression 3
  )  

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

for(;expression2; expression3)

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

for(;;)  

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

for( i = 1; i < 10; i++)

i = 1i 更新为1,然后检查条件。 i 的更新只进行一次,然后通过表达式i++ 进行更新。

【讨论】:

【参考方案5】:
for ( argc--, argv++; argc > 0; argc--, argv++ )  
 ... 

执行以下操作:

    执行“初始化”部分:递减argc并递增argv 检查是否argv &gt; 0,如果不是则退出循环 执行 ... 执行“更新”部分:递减argc并递增argv 转到上面的第 2 步

由于“初始化”和“更新”是一样的,所以也可以写成

while (argc--, argv++, argc > 0)
 ... 

这个表达式

(argc--, argv++, argc > 0)

由三个以the comma-operator分隔的子表达式组成。

这些子表达式从左到右执行。

整个表达式的计算结果是最右边的子表达式的结果。

【讨论】:

【参考方案6】:

argv 拥有命令行参数。但是,第一个是程序的名称。

因此,循环从argv[1] 开始,并处理命令行给出的每个参数,而不处理程序的名称

【讨论】:

【参考方案7】:
for ( argc--, argv++; argc > 0; argc--, argv++ )

可以读作

for ( (argc--), argv++; argc > 0; (argc--), argv++ )

因为逗号运算符的优先级最低,所以总是先计算左边的运算符

【讨论】:

【参考方案8】:

for循环中的初始化参数并不意味着只初始化一个具有特定值的变量。

它也可以有一个或多个用逗号分隔的正则表达式。

希望对你有帮助!!

【讨论】:

【参考方案9】:

for ( argc--, argv++; argc &gt; 0; argc--, argv++ ) 表示循环以 argc,argv 的值开始,分别设置为负 1 和相应的初始值加 1。每次迭代都会减少和增加它们的值,并且一旦 argc 达到 0(意味着读取了所有输入参数)就会停止。

【讨论】:

“设置为”具有误导性。 不是,他们正在改变 认为动词to minusto plus在英语中不存在。最好使用to be de/incremented by或类似的。一些母语为母语的人可能想在这里介入...: 是的,但动词set确实存在 我警告过阅读“设置为-1”和“设置为+1”的可能性。动词“设置”确实存在。然而,设置这两个值与“设置为高一的值”又称为“增量”和“设置为低一的值”又称为递减不同。在argc-- 之后,如果之前为 0,它只会以 -1 结束。我知道你的意思,但是在 *** 上写答案需要尽可能少地被误解。如果发现对您所写内容的误解,则由您决定如何反应。

以上是关于for循环的初始化和增量部分中的逗号如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

20 循环控制

php循环语句

逗号分隔的表达式作为 for 循环中的条件如何工作?

循环结构

for语句

for循环及break和continue的区别