printf is not just another function call.
Its signature stops describing the interesting part:
int printf(const char *fmt, ...);The fixed prefix says there is a format string. The ellipsis says the rest of the typed parameter list is missing. The caller may pass an int, a double, a pointer, a promoted char, a promoted float, or a sequence selected by another layer. The callee does not receive named source-level parameters for those trailing values. It receives ABI state and a va_list mechanism that must recover them later.
That is the claim: varargs are not merely flexible syntax. They are a function-call protocol with missing type information.
A small artifact shows the problem:
int call_printf_double(double x) {
return printf("x=%f\n", x);
}On System V AMD64, the format string goes in rdi, the double is in xmm0, and the caller sets al to indicate vector-register usage for a call that may reach a variadic callee. That AL marker is not visible in the C signature, but the variadic machinery depends on it.
Windows x64 solves a related problem differently. For a vararg or unprototyped call, a floating-point value must be duplicated into the corresponding general-purpose register as well as the floating-point register. In the printf(”x=%f\n”, x) shape, the wrapper can move the floating-point value into the right XMM lane and also copy its bits into the matching integer register.
Same source value. Different transport rules.
The format string does not replace the ABI. It tells printf what to request. It does not place the unnamed double in an XMM register, duplicate it into a general-purpose register, set AL, create a register-save area, or put overflow arguments on the stack in the expected location.
The boundary is FFI. A foreign-function bridge can often model a fixed signature:
void log_f64(const char *label, double value);A variadic function asks it to model a family of call shapes. Each unnamed suffix can change register usage, stack usage, and ABI-specific metadata. The conservative rule is boring because it is correct: avoid exposing variadic C APIs across language boundaries when fixed wrappers are practical.
Read the full essay on lospino.so: Varargs: The Function Call With Missing Type Information
Original essay published on lospino.so on 2026-06-04. This Substack dispatch is an adapted pointer to the canonical version, not a mirrored copy.

