I can't give a definitive answer, but my guess would be that it's because in the for loop case, there are three different expressions, each of which you may or may not need to use depending on the loop.
In the if case, it would make no sense if there were no expression; it would need to always act as if the expression were true or false, and thus be equivalent to just one of the clauses alone. In the while case, there would be a meaningful interpretation of while () { }, which would be to evaluate to while (1) { } (giving you a loop that you can break out of with break), but I guess that the benefits of leaving out that single character are not worth the trouble.
However, in the for loop case, there are three different expressions, each of which may or may not be needed. For instance, if you want to initialize and increment something on every loop, but will use break to break out of the loop, you can write for (i = 0; ; ++i), or if you just want the test and increment, because your variable is already initialized, you could write for (; i > 0; --i). Given that each of these expressions may not be necessary depending on your loop, making you fill in a placeholder for all that you do not use seems a bit cumbersome; and for consistency, rather than requiring a placeholder in one of them, all of them are optional, and the condition is considered to be a constant non-zero value if omitted.
Of course, it is sometimes easy to read too much intent into a design decision. Sometimes, the reason for a given standard is simply that that's how it was implemented in the first implementation, and everyone else just copied that. For an example, see Rob Pike's explanation of why files starting with . are hidden in Unix; it wasn't due to a deliberate design decision, but simply because someone took a shortcut when writing ls and didn't want to display the . and .. directory entries every time.