While it might seem like a down side, processing the format string at runtime is great for translation. To switch languages, a program can simply substitute in different format strings in its printf call. If the printf implementation supports positional arguments, then the translation can even reorder the interpolated values.
In contrast, schemes like C++'s IO streams based output (which sounds a lot like what you'd expect the compiler to convert a printf call to) end up with lots of short fragments of text that are harder to translate as a unit.