Why does printf require a required parameter?
The format string is required because macros work with variable C arguments depending on at least one argument present and using it to find others.
In particular, to read other variables, you use va_start
(then
va_arg
several times, once for each variable argument you want to read). When you call
va_start
, you need to pass it a format string (or more generally, the last parameter not changing for the function).
For example, this acts like printf
, but prints to both stdout
, and another file of your choice:
void tee(FILE *f, char const *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
va_start(ap, fmt);
vfprintf(f, fmt, ap);
va_end(ap);
}
It uses vprintf
and vfprintf
, therefore, it does not (directly) use va_arg
itself, onlyva_start
and
va_end
, but this is enough to show how the
fmt
use of
va_start
...
At one time it was actually unnecessary. When C was shiny and new, you might have a function equivalent to: int f(...);
.
va_start
,
va_arg
,
va_end
), Which requires at least one named parameter. Older macros put a number of requirements in the calling convention:
- Parameters are always passed in the same way, regardless of type or number.
- It is always easy to find the first argument that was passed.
With the usual C conditional call (all arguments are passed on the stack, arguments are moved from right to left), this was correct. You basically just looked at the top of the stack, navigated back to the return address, and there was the first argument.
With other calling conventions, things were not so simple. For example, just pushing arguments from left to right means that the first argument (format string, in case printf
) is like an arbitrary distance down the stack with an arbitrary number of other arguments after it.
What they ran into was to pass directly the previous (named) argument va_start
(and va_start is the macro that usually uses the address of that argument). If you push from right to left, this will give you the address, regardless of the distance the stack needs, then
va_arg
can go back to the stack to get other variables.
This was apparently seen as an acceptable compromise, especially since functions that take variable arguments almost always take at least one named parameter.
source to share
Required as it is printf
used to print data. Imagine what happens if you don't print anything. Nothing. So why remove this setting?
Same thing in scanf
: do you need to read the data somehow and how are you going to do it if you don't know the format of that data?
Some functions have no parameters because they don't need them, for example
void Hello(void) { puts("Hello"); }
Thus, they can survive without parameters. About printf
:
int printf(void) { //imaginary function, don't use it!
// WTF? What to print?
// Absolutely nothing! What the purpose then?
return smth;
}
Then this one printf
is completely useless if no arguments are passed .
source to share
In general, functions with an unknown number of arguments rely on va_start
, va_arg
and va_end
to handle arguments that are not explicitly listed in the function's parameter list.
va_start
needs the last named parameter to work. Therefore, a function that has an unknown number of arguments must have at least one named argument.
For printf
option / argument defining the format specification is the best choice as the required option / argument.
source to share
Without a description of the format, printf did not understand what to print. For C, everything is just bytes, so printf has no idea what data is being passed to it, and therefore has no idea how to represent it.
When you're new to C, you still don't know how true this is, especially if you've learned a language in which print () understands the type of data it sees.
source to share