All this stems from C strings being zero-terminated.
This in turn stems from the dedicated CPU support for working with zt strings that traces all the way back to PDP-11. So what C does here is exactly what it has always been doing - it provides a thin wrapper of the existing hardware functionality.
The variadic arguments are of the same nature - they basically allow for manual call stack parsing, again something that is a level down from the application code.
It's also easy to see how an API like sprintf and scanf came about - someone's just got tired of writing a bunch of boilerplate code to print a float with N decimals aligned to the left with a plus sign. So they threw together a function call "spec" (the format string), added a call stack parsing support (va_args) and - voila - a beautifully concise print/scan interface. It is a very clever construct, you've gotta give it that.
The flip side that it required people to pay close attention how they use it, which wasn't that bold of a requirement back
then. But as time went on, the average skill of C programmers went down, their use of the language did too, so more and more people started to step on the same rakes.
So, here we are. Zero-terminated strings are forbidden and va_args calls are nothing short of the magic.