Naively, the array address in the loop must be calculated
by a multiplication: the address required to access
people[i].age is:

people + i * sizeof(struct Person) + sizeof(char*)

However, on most machines, that multiplication would take a while to
calculate. At least a couple ofcycles, which is annoying since the rest
of the loop probably doesn't take much more than that to execute.

If our compiler is clever enough, and most of them are, it'll spot
that there's a faster way to calculate that address. Between each
successive iteration of the loop, that address increases by a nice,
constant value: sizeof(struct Person). So
instead of performing a slow multiplication, the compiler can just add
sizeof(struct Person) to the value that it
calculated on the last iteration.

The code the compiler produces would be equivalent to the code it would produce for:

Note that we've introduced a loop-carried state element (person_pointer)
that wasn't there before, and that where the loop previously had an
expensive multiplication, it now only has an addition (which, on most
architectures, can probably be merged with the load operation, saving
even more on complexity).

This strength reduction can be applied to any function which
can be expressed as a simpler, iterative function. For example,
to compute a power series, we must calculate
xn (which can be quite expensive to calculate) for all values of n from 0 onwards,
but each can be calculated simply from the last value by simplying
multiplying by x.