如何在C ++“ for”循环中放置两个增量语句?


93

我想在for-loop条件下增加两个变量,而不是一个。

所以像这样:

for (int i = 0; i != 5; ++i and ++j) 
    do_something(i, j);

这是什么语法?

Answers:


154

一个常见的习惯用法是使用逗号运算符,该运算符对两个操作数求值,然后返回第二个操作数。从而:

for(int i = 0; i != 5; ++i,++j) 
    do_something(i,j);

但这真的是逗号运算符吗?

写完这些之后,评论者建议它实际上是for语句中的一些特殊语法糖,而不是逗号运算符。我在GCC中进行了如下检查:

int i=0;
int a=5;
int x=0;

for(i; i<5; x=i++,a++){
    printf("i=%d a=%d x=%d\n",i,a,x);
}

我期望x能够拾取a的原始值,所以它应该已经为x显示了5,6,7..。我得到的是这个

i=0 a=5 x=0
i=1 a=6 x=0
i=2 a=7 x=1
i=3 a=8 x=2
i=4 a=9 x=3

但是,如果我将表达式放在方括号中以强制解析器真正看到逗号运算符,则会得到此提示

int main(){
    int i=0;
    int a=5;
    int x=0;

    for(i=0; i<5; x=(i++,a++)){
        printf("i=%d a=%d x=%d\n",i,a,x);
    }
}

i=0 a=5 x=0
i=1 a=6 x=5
i=2 a=7 x=6
i=3 a=8 x=7
i=4 a=9 x=8

最初,我认为这表明它根本不表现为逗号运算符,但事实证明,这只是一个优先级问题-逗号运算符的优先级最低,因此表达式x = i ++,a ++有效解析为(x = i ++),a ++

感谢所有评论,这是一次有趣的学习经历,我已经使用C多年了!


1
我已经读过好几次了,for循环的第一部分或第三部分中的逗号不是逗号运算符,而只是一个逗号分隔符。(但是,由于我在解析C ++语言标准方面特别不擅长,因此我没有找到官方的消息源。)
Daniel Daranas,2009年

我首先以为您不正确,但是我写了一些测试代码,您是正确的-它的行为不像逗号运算符。会修改我的答案!
保罗·迪克森

19
在这种情况下,它一个逗号运算符。无法获得期望的原因是命令运算符的优先级低于赋值运算符的优先级,因此,如果没有括号,它就解析为(x = i ++),j ++。
caf

6
它是一个逗号运算符。分配关系比逗号运算符更牢固,因此将解析x = i ++,a ++(x = i ++),a ++,而不解析x =(i ++,a ++)。某些库滥用了该特性,因此v = 1,2,3; 这样做很直观,但这仅是因为v = 1返回了一个代理对象,对于该对象,重载的逗号运算符对其进行附加。
AProgrammer

3
好。在open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2857.pdf第6.5.3节中,最后一部分是“表达式”。(尽管1.6#2将“表达式列表”定义为“用逗号分隔的表达式列表”,但此构造未出现在6.5.3中。)这意味着当我们编写“ ++ i,++ j”时,它本身必须是一个表达式,因此“,” 必须是逗号运算符(5.18)。(这不是“初始化程序列表”或“函数参数列表”,如5.18#2所说,这是“逗号具有特殊含义”的示例。)我觉得有点混乱。
丹尼尔·达拉纳斯

55

试试这个

for(int i = 0; i != 5; ++i, ++j)
    do_something(i,j);

17
+1您也可以在第一部分中声明j。for(int i = 0,j = 0; i!= 5; ++ i,++ j){...}
Daniel Daranas,2009年

1
+1作为一个旁注,这种相同的语法在C#中也有效(我从Google搜索“ C#for loop incrament 2 counters”中获得,所以我认为我会提到这一点)。
CodingWithSpike 2012年

@CodingWithSpike:嗯,在C#中逗号特殊的,逗号运算符表达式出现在那里实际上是不合法的。在C ++中合法使用逗号运算符但被C#拒绝的示例:for( ; ; ((++i), (++j)) )
Ben Voigt

@BenVoigt与逗号无关。这也不是合法的C#:for(int i = 0; i != 5; (++i)) {多余的括号使编译器认为它不再是“递增”操作。
CodingWithSpike

@CodingWithSpike:是的,但是括号改变了C#如何看逗号并防止for动作中的特殊含义。
Ben Voigt

6

尽量不要这样做!

http://www.research.att.com/~bs/JSF-AV-rules.pdf

AV规则199
for循环中的增量表达式除了将单个循环参数更改为该循环的下一个值外,将不执行任何操作。

理由:可读性。


4
没错,但公平地说,我很确定规则标准是为战斗机中的嵌入式软件而不是为花园C(++)程序编写的。话虽如此,这可能是一种良好的可读性习惯,而且谁知道,也许您将在设计F-35软件,这将是一种减少习惯。
galois 2015年


2

我来这里是为了提醒自己如何将第二个索引编码到FOR循环的增量子句中,我知道主要可以通过观察并结合到另一个用C ++编写的项目中的示例来完成。

今天,我在C#中工作,但是我确信在这方面它将遵循相同的规则,因为FOR语句是所有编程中最早的控制结构之一。值得庆幸的是,我最近花了几天的时间来精确地记录一个较旧的C程序中的FOR循环的行为,然后我很快意识到那些研究吸取了适用于当今C#问题(尤其是第二个索引变量的行为)的课程。 。

对于粗心的人,以下是我的观察总结。通过仔细观察Locals窗口中的变量,我今天看到的所有事情都证实了我的期望,即C#FOR语句的行为与C或C ++ FOR语句完全一样。

  1. 第一次执行FOR循环时,将跳过增量子句(三个子句中的第三个)。在Visual C和C ++中,增量是在实现循环的块的中间以三条机器指令生成的,因此初始阶段仅运行一次初始化代码,然后跳过增量块以执行终止测试。这实现了一个功能,即FOR循环执行0次或多次,具体取决于其索引和限制变量的状态。
  2. 如果执行循环主体,则其最后一条语句是跳转到第一次迭代所跳过的三个增量指令中的第一个。这些执行之后,控制自然会落入实现中间子句的极限测试代码中。该测试的结果确定是否执行FOR循环的主体,或者控制是否通过其范围底部的跳转转移到下一条指令。
  3. 由于控制从FOR循环块的底部转移到增量块,因此在执行测试之前将索引变量递增。这种行为不仅解释了为什么必须按照学习的方式对limit子句进行编码,而且还会影响通过逗号运算符添加的任何次要增量,因为它成为第三个子句的一部分。因此,它不会在第一次迭代中更改,但是会在最后一次迭代中更改,而永远不会执行主体。

如果在循环结束时您的任何一个索引变量仍在作用域内,则对于真正的索引变量,它们的值将比停止循环的阈值高一个。同样,例如,如果第二个变量在进入循环之前被初始化为零,则它的末尾值将是迭代计数,假设它是一个增量(++),而不是一个减量,并且在其中没有任何值循环的主体会更改其值。


1

我同意squelart。增加两个变量很容易发生错误,特别是如果仅测试其中之一。

这是执行此操作的可读方法:

int j = 0;
for(int i = 0; i < 5; ++i) {
    do_something(i, j);
    ++j;
}

For循环适用于循环在一个递增/递减变量上运行的情况。对于任何其他变量,请在循环中进行更改。

如果需要j绑定i,为什么不保留原始变量并添加i

for(int i = 0; i < 5; ++i) {
    do_something(i,a+i);
}

如果您的逻辑更为复杂(例如,您实际上需要监视多个变量),则可以使用while循环。


2
在第一个示例中,j比i增加了一次!那么对于迭代器,在前x个步骤中需要对其进行一些操作的迭代器呢?(而且集合总是足够长)您可以在每次迭代时都提升迭代器,但是恕我直言。
彼得·史密斯


0

使用数学。如果这两个运算在数学上取决于循环迭代,那么为什么不进行数学运算呢?

int i, j;//That have some meaningful values in them?
for( int counter = 0; counter < count_max; ++counter )
    do_something (counter+i, counter+j);

或者,更具体地参考OP的示例:

for(int i = 0; i != 5; ++i)
    do_something(i, j+i);

尤其是如果您要按值传递函数,那么您应该得到完全符合您想要的功能的东西。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.