# For-loop-insanity

This topic is 1263 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Classic mistake. There is a generic way to make it work, if you really want to, though:

uint8_t j = 0;

do
{
printf("%i\n", j);
}
while (++j);


Which is pretty readable, since the while loop is clearly going to terminate as soon as j overflows. And like Ravyne said, it doesn't really make anything faster in general, that said I personally don't see this as an optimization tactic, but more as a type correctness thing, in other words, the loop variable is going to be used as an 8-bit unsigned integer, so let's not lie to ourselves, make it an 8-bit unsigned integer. But with this comes the responsibility of watching out for overflow and other low-level things which generally don't come up when simply using a sufficiently large signed integer like int, people will have their own opinions on that matter

edit: fixed the termination condition error, thanks

##### Share on other sites

I think we all have those moments sometimes. Figuring out why those loops would never terminate could possibly make for a very frustrating debug session.

##### Share on other sites

Classic mistake. There is a generic way to make it work, if you really want to, though:

uint8_t j = 0;

do
{
printf("%i\n", j);
}
while (j++);


Which is pretty readable, since the while loop is clearly going to terminate as soon as j overflows. And like Ravyne said, it doesn't really make anything faster in general, that said I personally don't see this as an optimization tactic, but more as a type correctness thing, in other words, the loop variable is going to be used as an 8-bit unsigned integer, so let's not lie to ourselves, make it an 8-bit unsigned integer. But with this comes the responsibility of watching out for overflow and other low-level things which generally don't come up when simply using a sufficiently large signed integer like int, people will have their own opinions on that matter

Don't you mean ++j?

I just tried it on ARM7, which has only 32-bit registers, and it compiled to a compare with 256, branch if not equal. So it doesn't actually force the wrapping, but did get the logic correct, and no different than using an int and comparing with 256.

##### Share on other sites

Don't you mean ++j?

Yes, he does :)

##### Share on other sites

Huh. I have ++j in my test source file, must've copied it wrong on the forum. Sorry about that, I will edit.

To be picky, this works, but it also leaks the loop counter into the surrounding scope (as a "correctness issue"), and might contribute to register pressure for the duration of the scope (as an "optimization issue", though I would hope a modern compiler would do better)

About leaking the loop counter in the surrounding scope, I am curious if there is an elegant way to achieve the same result with a for loop, so that the loop counter would be limited to the scope of the loop.

##### Share on other sites

I am curious if there is an elegant way to achieve the same result with a for loop, so that the loop counter would be limited to the scope of the loop.

Elegant, I don't think so. This is the least ugly thing I could come up with.

for (uint8_t test = 1; test > 0; test++) {
printf("%i\n", (test - 1));
}

But please don't try this at home :)

You could also just create a scope around the variable definition and do-while in your example. It would limit the scope 'appropriately', but its ugly, non-idiomatic, and another programmer who comes along might be tempted to delete the extra braces and not think anything of it -- likely that just puts you back in the same potentially-sub-optimal boat as before, but it could break a build or change the behavior of the code if the variable name aliased another.

##### Share on other sites

The elegant way is to just use an int.

CPUs don't have 8bit registers, or don't want to operate on 8bit values these days, which means that when you write this:

uint8_t j = 0; do {...} while (++j);

You're actually asking the compiler to transform it into something like this abomination:
int j = 0; do {...} while( (j=(j+1)&0xFF) != 0 );

...and then also asking the compiler to try and optimize such horrible code for you!

After wasting a bunch of time optimizing your code, it will be the same as if you'd just written what DekuTree posted, which is a perfectly idiomatic loop without any clever obfuscation applied:

for( int i=0; i!=256; ++i ) {...}

If there is a more optimal way of looping on a specific platform, a clever compiler will be able to transform any of the above loops to it, seeing they're all exactly equivalent in behavior!

Edited by Hodgman

##### Share on other sites

Maybe more to the point -- for a local variable, one that will likely be allocated as a register (or on the stack, at worst), you don't really gain anything at all by making 'test' small -- there might be reasons of correctness that are worthwhile, but optimization is basically a non-factor. Maybe if the loop appears in a deeply-recursive function, or on a small, embedded system with 4k of memory or something, but not usually. Just use the natural word-size of the machine, usually plain old 'int'  -- it will be as fast as (faster, more likely) than the smaller value.

I totally agree with this.  I do usually use int, but sometimes it happens that I want to ensure a certain size just because of the value I am testing against.  This is not the case here though.  Those cases would more likely be uint64_t, even if I would never make anything that iterates that many times...

Classic mistake. There is a generic way to make it work, if you really want to, though:

uint8_t j = 0;

do
{
printf("%i\n", j);
}
while (++j);


Which is pretty readable, since the while loop is clearly going to terminate as soon as j overflows. And like Ravyne said, it doesn't really make anything faster in general, that said I personally don't see this as an optimization tactic, but more as a type correctness thing, in other words, the loop variable is going to be used as an 8-bit unsigned integer, so let's not lie to ourselves, make it an 8-bit unsigned integer. But with this comes the responsibility of watching out for overflow and other low-level things which generally don't come up when simply using a sufficiently large signed integer like int, people will have their own opinions on that matter

edit: fixed the termination condition error, thanks

Ah this is actually quite clever, lol...  I would never write anything like that though, but it is a interesting point of view.  :D  My solution was, of course, to use an int instead.

##### Share on other sites

Since we are trying to come up with new, obscure, and complicated ways for counting up, how about this

Somehow I can see Java programmer actually doing this.

##### Share on other sites
To be honest, most modern languages have some sort kind of range-based looping constructs...

##### Share on other sites

This is hardly obscure, (x)range in Python works that way.

Lua for loop kind of does too, modifying the iterator doesn't matter, so in meaning it's closer to range like that really.

Anyone who knows range loop syntax could read that easily. Only question is if it's inclusive or not (which is a real problem, as shown in <random> header's distribution classes).

While we are in topic of loops, this is an example from one zlib game codebase I found online:

#define loop(v,m) for(int v = 0; v<int(m); v++)
#define loopi(m) loop(i,m)
#define loopj(m) loop(j,m)
#define loopk(m) loop(k,m)
#define loopl(m) loop(l,m)
#define loopirev(v) for(int i = v-1; i>=0; i--)


Another nice example is trying to count back from size-1 to 0 using int with >=0 condition, and then, (because someone complains that you should always use std::size_t for indices because their holy compiler that clearly never is wrong or stupidly overzealous when ran with -Wall said so) changing type to std::size_t with same condition so it wraps around and never ends.

##### Share on other sites

This is hardly obscure, (x)range in Python works that way.

Lua for loop kind of does too, modifying the iterator doesn't matter, so in meaning it's closer to range like that really.

Anyone who knows range loop syntax could read that easily. Only question is if it's inclusive or not (which is a real problem, as shown in <random> header's distribution classes).

While we are in topic of loops, this is an example from one zlib game codebase I found online:

#define loop(v,m) for(int v = 0; v<int(m); v++)
#define loopi(m) loop(i,m)
#define loopj(m) loop(j,m)
#define loopk(m) loop(k,m)
#define loopl(m) loop(l,m)
#define loopirev(v) for(int i = v-1; i>=0; i--)


Another nice example is trying to count back from size-1 to 0 using int with >=0 condition, and then, (because someone complains that you should always use std::size_t for indices because their holy compiler that clearly never is wrong or stupidly overzealous when ran with -Wall said so) changing type to std::size_t with same condition so it wraps around and never ends.

Why would one redefine the language like that?  Too much to type?  It is almost like you need to learn a new language inside the language you already know, lol...  Unless I completely missed a point.

##### Share on other sites

I guess it's that, the codebase (while technically C++) isn't too commented and is a bit horror-y and very old (10+ years I think) so it's written in very C style with macros, weird casts, relying on memory layout of structs, TONS of globals, etc.

##### Share on other sites

Why would one redefine the language like that? Too much to type? It is almost like you need to learn a new language inside the language you already know, lol... Unless I completely missed a point.

What do you mean? Everyone I know codes in Better C!

##### Share on other sites

Why would one redefine the language like that? Too much to type? It is almost like you need to learn a new language inside the language you already know, lol... Unless I completely missed a point.

What do you mean? Everyone I know codes in Better C!

It actually doesn't look that stupid when you put it like that.

I was thinking to add things like:

#define BEGIN {

#define END }

So it looks a bit more like Pascal too.

Edited by aregee

##### Share on other sites

Why would one redefine the language like that? Too much to type? It is almost like you need to learn a new language inside the language you already know, lol... Unless I completely missed a point.

What do you mean? Everyone I know codes in Better C!

It actually doesn't look that stupid when you put it like that.

I was thinking to add things like:

#define BEGIN {

#define END }

So it looks a bit more like Pascal too.

That's exactly what Bourne did when he wrote his original shell, because he was more familiar with ALGOL.

##### Share on other sites

Oh dear, that is Pascal...  no, it is C...  :D  Absolutely hilarious these <what do we call them>?  If I remember correctly, Pascal had assignment operator  '<variable> := <value>;' ? (colon+equality)?

It actually doesn't look that stupid when you put it like that.

I was thinking to add things like:

#define BEGIN {

#define END }

So it looks a bit more like Pascal too.

That's exactly what Bourne did when he wrote his original shell, because he was more familiar with ALGOL.

Oh dear...  I am not old enough for ALGOL.  :D  Bourne, I have heard of, actually.  He made the predecessor to BASH (Bourne Again SHell).

I never thought I would learn and laugh so much for posting a silly for-sentence in a forum, lol.  :D

##### Share on other sites

Wait, is it me or did nobody point out that the compiler would have also complained about that second loop in the first post? (reason: condition is always true)

##### Share on other sites

Wait, is it me or did nobody point out that the compiler would have also complained about that second loop in the first post? (reason: condition is always true)

Xcode did certainly not complain.  Let me double check that.

Nope.  No complaints.  Xcode is happy...  I even tried the static analyzer, and it just replied "Build Succeeded".  "No Issues".