108

I would like to increment two variables in a for-loop condition instead of one.

So something like:

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

What is the syntax for this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Peter Smit
  • 27,696
  • 33
  • 111
  • 170

8 Answers8

187

A common idiom is to use the comma operator which evaluates both operands, and returns the second operand. Thus:

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

But is it really a comma operator?

Now having wrote that, a commenter suggested it was actually some special syntactic sugar in the for statement, and not a comma operator at all. I checked that in GCC as follows:

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);
}

I was expecting x to pick up the original value of a, so it should have displayed 5,6,7.. for x. What I got was this

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

However, if I bracketed the expression to force the parser into really seeing a comma operator, I get this

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

Initially I thought that this showed it wasn't behaving as a comma operator at all, but as it turns out, this is simply a precedence issue - the comma operator has the lowest possible precedence, so the expression x=i++,a++ is effectively parsed as (x=i++),a++

Thanks for all the comments, it was an interesting learning experience, and I've been using C for many years!

Paul Dixon
  • 295,876
  • 54
  • 310
  • 348
  • 1
    I've read several times that the comma in the first or third part of a for loop is *not* the comma operator, but just a comma separator. (However, I fail to find an official source for this, since I'm particularly bad at parsing the C++ language standard.) – Daniel Daranas Aug 05 '09 at 10:02
  • I first thought you were incorrect, but I wrote some test code and you are correct - it does not behave like a comma operator. Will amend my answer! – Paul Dixon Aug 05 '09 at 10:27
  • +1 for your ammendment. It's a bit counter intuitive to me but most of the times when a comma appears, it's not a comma operator, and this is one of them. – Daniel Daranas Aug 05 '09 at 10:42
  • 22
    It *is* a comma operator in that context. The reason you aren't getting what you expect is that the command operator has lower precedence than the assignment operator, so without the parantheses it parses as (x = i++), j++. – caf Aug 05 '09 at 11:09
  • 6
    It IS a comma operator. The assignation ties more strongly than the comma operator, so x=i++,a++ is parsed (x=i++),a++ and not x=(i++, a++). That characteristic is misused by some libraries so that v = 1,2,3; does the intuitive things, but only because v = 1 returns a proxy object for which the overloaded comma operator does an append. – AProgrammer Aug 05 '09 at 11:11
  • And now I'm back where I started. Which is good, as I've thought it was comma operator for 20 years! – Paul Dixon Aug 05 '09 at 11:18
  • I totally fail to find an explanation in the standard. Some google results say it is a comma operator, some say it isn't. Your example doesn't prove that it actually *is* a comma operator, only that it can be. I remember being told that it wasn't some years ago at the borland.cpp.language newsgroup, but I can't search it anymore. Maybe this could be my first question on Stack Overflow, since every time I think of a question, it already exists, and this does not seem to be the case. – Daniel Daranas Aug 05 '09 at 11:27
  • 3
    Ok. From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2857.pdf section 6.5.3 the last part is an "expression". (Although 1.6 #2 defines an "expression-list" as a "list of expressions separated by commas", this construct does not appear in 6.5.3.). This means that when we write "++i,++j" it has to be an expression in an by itself, and thus "," *must be the comma operator* (5.18). (This is not a "list of initializers" or "list of arguments to functions", which are examples where "comma is given a special meaning", as 5.18 #2 says.). I find it a bit confusing though. – Daniel Daranas Aug 05 '09 at 11:50
  • Interesting discussion. Another question is, does it matter whether it is a comma operator or some special syntactic sugar in the for statement? Would there be any difference? – HelloGoodbye May 28 '18 at 07:55
  • While the OP asks for increment, it doesn't specify if the increment exactly equals one. How would we write these multiple increments with bigger increments ? Do we have to write the equal signs ? ```c for(int n = 0; n < 5; n+=(m+=STEP, k+=STEP, STEP)) ``` or ```c for(int n = 0; n < 5; n=(m+=STEP, k+= STEP, n+=STEP)) ``` ? – Dimitri Lesnoff Oct 25 '22 at 12:37
  • Not sure what you're trying to do there, it looks like you've misunderstood where this question wandered off into exploring the precise nature of the syntax. Just separate your increments with a comma, e.g. `for(int n = 0; n < 5; m+=STEP, k+= STEP, n+=STEP)` – Paul Dixon Oct 26 '22 at 14:06
62

Try this

for(int i = 0; i != 5; ++i, ++j)
    do_something(i,j);
yeyeyerman
  • 7,751
  • 7
  • 43
  • 52
  • 19
    +1 You can also declare j in the first part. for(int i = 0, j = 0; i != 5; ++i, ++j) { ... } – Daniel Daranas Aug 05 '09 at 09:46
  • 1
    +1 As a side-note, this same syntax works in C# (I got here from a Google search for "C# for loop incrament 2 counters" so thought I would mention this). – CodingWithSpike Aug 14 '12 at 14:44
  • @CodingWithSpike: Well, in C# the comma *is* special, it's not actually legal for a comma operator expression to appear there. Example that's legal usage of comma operator in C++, but rejected by C#: `for( ; ; ((++i), (++j)) )` – Ben Voigt Oct 05 '17 at 19:58
  • @BenVoigt has nothing to do with the comma. This isn't legal C# either: `for(int i = 0; i != 5; (++i)) {` The extra parenthesis trick the compiler into thinking it isn't an "increment" operation any more. – CodingWithSpike Oct 11 '17 at 13:30
  • @CodingWithSpike: That's true, but the parentheses *also* change how C# sees the comma and prevents the special meaning inside the for action. – Ben Voigt Oct 11 '17 at 14:48
5

Try not to do it!

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

AV Rule 199
The increment expression in a for loop will perform no action other than to change a single loop parameter to the next value for the loop.

Rationale: Readability.

Brian
  • 109
  • 4
squelart
  • 11,261
  • 3
  • 39
  • 43
  • 5
    That's true, but to be fair I'm pretty sure that standard of rules was written for embedded software in a fighter jet, not the garden variety C(++) program. That being said, it's probably a good readability habit to get into, and who knows, maybe you'll be designing F-35 software, and it will be one less habit to break. – galois Dec 27 '15 at 10:19
4
for (int i = 0; i != 5; ++i, ++j) 
    do_something(i, j);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
malay
  • 1,434
  • 4
  • 17
  • 28
2

I came here to remind myself how to code a second index into the increment clause of a FOR loop, which I knew could be done mainly from observing it in a sample that I incorporated into another project, that written in C++.

Today, I am working in C#, but I felt sure that it would obey the same rules in this regard, since the FOR statement is one of the oldest control structures in all of programming. Thankfully, I had recently spent several days precisely documenting the behavior of a FOR loop in one of my older C programs, and I quickly realized that those studies held lessons that applied to today's C# problem, in particular to the behavior of the second index variable.

For the unwary, following is a summary of my observations. Everything I saw happening today, by carefully observing variables in the Locals window, confirmed my expectation that a C# FOR statement behaves exactly like a C or C++ FOR statement.

  1. The first time a FOR loop executes, the increment clause (the 3rd of its three) is skipped. In Visual C and C++, the increment is generated as three machine instructions in the middle of the block that implements the loop, so that the initial pass runs the initialization code once only, then jumps over the increment block to execute the termination test. This implements the feature that a FOR loop executes zero or more times, depending on the state of its index and limit variables.
  2. If the body of the loop executes, its last statement is a jump to the first of the three increment instructions that were skipped by the first iteration. After these execute, control falls naturally into the limit test code that implements the middle clause. The outcome of that test determines whether the body of the FOR loop executes, or whether control transfers to the next instruction past the jump at the bottom of its scope.
  3. Since control transfers from the bottom of the FOR loop block to the increment block, the index variable is incremented before the test is executed. Not only does this behavior explain why you must code your limit clauses the way you learned, but it affects any secondary increment that you add, via the comma operator, because it becomes part of the third clause. Hence, it is not changed on the first iteration, but it is on the last iteration, which never executes the body.

If either of your index variables remains in scope when the loop ends, their value will be one higher than the threshold that stops the loop, in the case of the true index variable. Likewise, if, for example, the second variable is initialized to zero before the loop is entered, its value at the end will be the iteration count, assuming that it is an increment (++), not a decrement, and that nothing in the body of the loop changes its value.

David A. Gray
  • 1,039
  • 12
  • 19
1

I agree with squelart. Incrementing two variables is bug prone, especially if you only test for one of them.

This is the readable way to do this:

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

For loops are meant for cases where your loop runs on one increasing/decreasing variable. For any other variable, change it in the loop.

If you need j to be tied to i, why not leave the original variable as is and add i?

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

If your logic is more complex (for example, you need to actually monitor more than one variable), I'd use a while loop.

TamaMcGlinn
  • 2,840
  • 23
  • 34
Ran Halprin
  • 471
  • 5
  • 15
  • 2
    In the first example, j is incremented once more than i! What about an iterator where needs to be done some action on for the first x steps? (And the collection is always long enough) You can than ascend the iterator every iteration, but is is much cleaner imho. – Peter Smit Aug 05 '09 at 11:46
0
int main(){
    int i=0;
    int a=0;
    for(i;i<5;i++,a++){
        printf("%d %d\n",a,i);
    } 
}
Arkaitz Jimenez
  • 22,500
  • 11
  • 75
  • 105
0

Use Maths. If the two operations mathematically depend on the loop iteration, why not do the math?

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

Or, more specifically referring to the OP's example:

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

Especially if you're passing into a function by value, then you should get something that does exactly what you want.

xaviersjs
  • 1,579
  • 1
  • 15
  • 25