Explicit End Indexes in For Loops

This is another entry in the Strange Programmer Habits" series. In each article we investigate a strange-looking construction which has a legitimate, but non-obvious purpose.

In this article, we look at why we might want to set up the exit condition of a for loop when it's initialized. Let's start by looking at a "normal" loop in JavaScript:


   var str = "I am a test string";

   for( var i = 0; i < str.length; i++ ) {
     var char = str.substr( i, 1 );
     console.log( `Character ${i} is ${char}` );
   }

Nothing could be clearer: loop through each character in the string, printing out a message on the console for each character. But every now and again, you'll see something like this instead:


   for( var i = 0, il = str.length; i < il; i++ ) {
     var char = str.substr( i, 1 );
     console.log( `Character ${i} is ${char}` );
   }

Why did they do this? Should you do it to?

The answer to the second question is "probably not" if you're a JavaScript programmer and "it depends" if you're a C programmer.

In the early days of JavaScript, implementations were not horribly efficient. (Looking at you, Rhino.) Old implementations weren't smart enough to know strings like the one in the example above weren't changing each time through the loop. So it punted and called strlen() or Java's String.length() method each time through the loop.

By calculating the length once, assigning it to a variable and then looping through the loop, you could recover a small amount of execution time.

Most programmers probably didn't need to worry about this; they weren't coding nested loops that looped over large arrays. But the few programmers I met who did do this insisted they got a speed boost from it.

Your mileage may vary, though. Most modern JavaScript implementations are fast and can make decent guesses about whether or not an array or string is changing as you iterate across its members.

C programmers don't have it so easy, however. While JavaScript is single threaded except in well defined cases, C programs could be single-threaded or a multi-threaded, and the compiler might not know ahead of time. If you coded a loop that depends on the length of a string or an array being consistent across iterations, it's up to you to figure out if the memory you're accessing is getting changed by another thread.

Because the compiler can't guarantee a string's length won't change without warning, it won't aggressively memoize the results of function calls. If you execute this code in C, it will call strlen() each time through the loop.


   #include <stdio.h>
   #include <string.h>

   int main() {
     char * teststr = "I am a test string.";

     for( int i = 0; i < strlen( teststr ); i++ ) {
       printf( "Char %d is %c\n", i, teststr[i] );
     }

     return( 0 );
   }

So, if you know you're not in a multi-threaded environment, you might want to modify the loop to look something more like this. It'll only call strlen() once.


   for( int i = 0, il = strlen( teststr ); i < il; i++ ) {