If all you want is a simple counting loop, then
for (i=0; i<100; i++) dostuff();
will be fine, and the compiler can optimize it.
If you use a function in the continue part of the for statement, like
for (i=0; i<strlen(s); i++) dostuff();
then the function will be evaluated every time and this is usually not a good idea as the function overheads will slow your process. Sometimes it can slow your process to the point of unusability.
If the function's return value will not change during the iteration, extract it from the loop:
slen = strlen(s);
for (i=0; i<slen; i++) dostuff();
But there are times when the function will be returning different values each call, and then you do not want it extracted from the loop:
for (isread(fd, &buffer, ISFIRST);
isstat(fd) >= 0;
isread(fd, &buffer, ISNEXT)
{
dostuff(buffer);
}
and you want it evaluated each time. (That is a slightly contrived example based on work that I do, but it shows the potential).
C gives you the raw ability to roll your loop any way you can. You have to know how your loop is supposed to work, and you optimize it as best you can, depending on your needs.
That last example could have been expressed as a while loop:
isread(fd, &buffer, ISFIRST);
while (isstat(fd) >= 0)
{
dostuff(buffer);
isread(fd, &buffer, ISNEXT);
}
but it's not as neat, and if I use a continue in the loop, then I have to call the iterating isread again. Putting the whole thing in a for loop makes it neater, and ensures that the iterating isread is called each loop.
I write lower-level functions so they can be used in for loops like this. It brings all elements of the while loop together so you can understand it more easily.