C provides several ways to iterate—repeat a sequence of steps. Chief among them are while, do-while and for loops. Arduino adds the loop() function for the main body of a program; canonically C does not provide that one on its own.

Background

A while loop resembles the following:

// Initialization can go here
while (condition) {
  // Body of loop
}

condition” is a placeholder—this gets replaced with some true/false statement; perhaps comparing a variable against a number, or checking the return status of another function. As long as the statement evaluates as true, then the code in the body of the loop is executed again and again.

Initializing one or more variables before the loop is common, but not required of all programs.

A do-while loop moves the test condition to the end:

// Initialization can go here
do {
  // Body of loop
} while (condition);

condition”—the true/false statement—is evaluated after the body of the loop. So the loop always executes at least once.

Some logic just works better one way or the other.

A for loop is sort of a contraction of the while loop:

for (initialization; condition; operation) {
  // Body of loop
}

initialization” is any code that sets up the loop…for example, it’s very common to set up a counter variable here. 92% of for loops start with an “i=0” initialization (programmer humor, not a true statistic, but it’s quite common).

condition” is just like the while loop—a testable true/false statement, evaluated before each iteration of the loop. Very often tests a counter against some limit, e.g. i<10.

operation” is a simple statement that’s performed after each iteration of the loop body. Very often used to increment a counter, e.g. i++;

So three lines of code before now all fit into one. It’s a nice shorthand.

FUN FACT: conditional statements can also test integers for non-zero-ness. A variable with value 0 is equivalent to false. 1, 42, -303 or any other non-zero integers are true. Okay, maybe not fun, but it’s a fact.

for Shenanigans

Not often seen, hence occasional confusion, but any of the parts of a for loop are optional; they can just be omitted when not needed, as long as the other remaining syntax (semicolons, etc.) is observed. These are all valid for loops:

for (; condition; operation) {
  // Body of loop
}

for (initialization; ; operation) {
  // Body of loop
}

for (initialization; condition; ) {
  // Body of loop
}

for (initialization; condition; operation);
// I ain't got no body

while loops can also omit the body and just end with a semicolon, but do-while loops always have the braces.

Recent C++ compilers allow declaring temporary variables right inside the initialization of a for loop. This wasn’t always the case, so you don’t see it in older code. The “scope” of such variables is limited to the body of the loop (i.e. can’t be referenced after the loop).

One can also use a comma separator to pack more stuff into a line, e.g.

for (int a=0, b=1, c=2; a<10; a+=1, b+=2, c*=3) {
	Serial.printf("%d %d %d\n", a, b, c);
}

(Note: Serial.printf() is typically present only for 32-bit microcontrollers in Arduino. With Arduino UNO and other 8-bit devices, one must Serial.print() each value separately.)

Old Habits Die Hard

It’s very common in C/C++/Arduino to structure an infinite loop (that is, repeats until power is cut) like the following:

while (1) { // Always true; repeats forever
  // Body of loop
}

Occasionally there’s no body, if a program encounters an error and needs to “hang” because it can’t safely continue. The line just ends with a semicolon.

In the introduction, aged programmers, poor-quality compilers and code I wrote were all mentioned. Combined with a for loop’s aforementioned optional elements, this yields a valid but little-seen infinite loop syntax:

for (;;) {
  // Body of loop
}

I developed this habit because one particular compiler on one particular system decades ago would generate machine code that actually evaluates the truth of a while(1) statement every time, instead of seeing the obvious infinite loop and simply jumping without evaluating. The empty for was more efficient.

No compiler nowadays is that stupid, they will always do the right thing. But in the time and circumstances, it made programs a few cycles faster and a few bytes smaller.

I only stick with for(;;) because it looks cool and I get to tell Big Iron Stories…but it’s preferable for teachable code to use while(1) because that’s universally understood today as “infinite loop.”

This guide was first published on Aug 10, 2022. It was last updated on Mar 08, 2024.

This page (Funny “for” Loops) was last updated on Mar 08, 2024.

Text editor powered by tinymce.