Normally, function declarations insure that callers pass arguments in the format [that is, the type] expected by the called function. This happens so automatically that programmers take it for granted.
printf is a variadic function - each call to it can have a different number of arguments, and those arguments can be of different types each time the function is called. There is no simple and invariant rule for the compiler to follow regarding how the arguments are formatted.
Each variadic function has its own way of communicating the number and type of its arguments from caller to called. printf does this with the format string. The format specifiers within the format string represent the caller's promise of what the format [that is, the type] is for each argument. If the type of an argument doesn't match the format string then you have broken your promise and disaster ensues.
Carefully study the specification for how the printf format specifiers are interpreted. "%f" promises the corresponding argument is of type double; "%Lf" promises it is of type long double; I'm not sure "%lf" is even valid.
In general, each variadic function has its own way to communicate argument type from caller to called. The Standard does not require compilers to be aware of these rules -- not even for variadic functions specified by the Standard. Thus, you can't count on compile-time warnings to alert you to a bad format string. The problems caused by a bad format string aren't apparent until run-time.
Comment