Pointers and Tuning and Loops! Oh My!
Introduction While all code should be efficient, code for library-like components, especially involving loops, should be as efficient as possible since such code is often widely used. In my A Simple Dynamic Array for C , I included the source code for a function to clean-up a dynamic array: void array_cleanup ( array_t * array , array_free_fn_t free_fn ) { if ( array == NULL ) return ; if ( free_fn != NULL ) { char * element = array -> elements ; for ( size_t i = 0 ; i < array -> len ; ++ i ) { ( * free_fn )( element ); element += array -> esize ; } } free ( array -> elements ); array_init ( array , array -> esize ); } While this code is correct and good enough for pedagogical purposes, it’s not optimal. Before I tell you why, see if you can figure out why. Hint: it has to do with the use of both the array pointer and the function call in the loop. For those who might not get the reference in this article’s title, it’s a play on a line from The Wizard of Oz . The original line was “Lions and tigers and bears! Oh, my!” Loop Refresher In C (and languages inspired by C including C++, C#, Go, and Java), for loop conditions are reevaluated on every loop iteration. For example, in: for ( int i = 0 ; i < n ; ++ i ) the condition i < n is evaluated on every iteration. That means if n is changed within the loop, it could terminate either earlier or later than it was initially supposed to — and this is well-defined behavior. This is in contrast to some older languages like Fortran and Pascal as well as some newer languages like Rust where the loop’s termination value is evaluated once just prior to the start of the loop. For example, in Pascal: for i := 1 to n do is actually treated as if it were this: limit := n ; for i := 1 to limit do Note that if the loop condition is a more complicated expression, such as calling a function: for ( int i = 0 ; i < f ( x ); ++ i ) then the function will be called on every loop iteration. Except if the function is marked as unsequenced in C