The *zprintf
family of functions is
a modernized form of the *printf
family of functions.
The *printf
functions have a return type ‘int’
and therefore can only produce results that are up to (2 GiB - 1 byte) long.
The problem with this is not so much that it is an arbitrary limitation
(that persists even in processes that have, say, 50 GiB of RAM available).
The bigger problem is that in reliable programs,
it requires handling of an error code EOVERFLOW
that indicates a result whose size would be 2 GiB or larger.
How does a reliable program do error handling of *printf
function calls?
For output to strings and file descriptors
(such as sprintf
and dprintf
),
there is no other way than to check each such call.
For output to FILE
streams (such as fprintf
),
beginners are tempted to ignore the return value of each call
and instead check ferror (stream)
at the end.
The problem with this approach is that
at the moment the error is detected,
incorrect output has already been sent onto the stream.
So, in this case as well, the reliable approach is to check each such call.
The simple format strings that most programs use in 99% of the places,
namely with no wide string or wide character arguments,
nor with widths passed as int
argument,
can only fail with two possible error codes:
ENOMEM
, when
the result would be too large to allocate in the process’ memory.
EOVERFLOW
, when
the result is 2 GiB or larger but still allocatable.
Many GNU programs use “checking” wrappers (functions xvasprintf
, etc.)
that check for ENOMEM
and call xalloc_die
,
thus aborting the program in that case.
The problem is that EOVERFLOW
is not handled, even with such wrappers.
Should EOVERFLOW
be handled like ENOMEM
, by aborting the program?
No, as mentioned above, that would be an arbitrary limitation, which the
GNU Coding Standards
urge us to avoid.
The *zprintf
functions are like the *printf
functions,
except that the return type is
ptrdiff_t
instead of int
,
for output to strings,
off64_t
(which is always equivalent to int64_t
)
instead of int
,
for output to file streams and file descriptors.
Thus, for these functions, EOVERFLOW
cannot occur
(except for format strings which take widths as argument,
which we have excluded above),
and the “checking” wrappers (functions xvasprintf
, etc.)
are thus sufficient for ensuring an error-free result.
Note:
In 64-bit processes, ptrdiff_t
is 64 bits wide,
i.e. equivalent to int64_t
.
In 32-bit processes, ptrdiff_t
is only 32 bits wide,
but since in these environments,
memory regions of 2 GiB or larger cannot be allocated anyway
(malloc
would fail with ENOMEM
),
this type is sufficient.
The following Gnulib functions and modules exist:
Original function | Modernized function | Modules |
---|---|---|
sprintf | szprintf | szprintf , szprintf-posix , szprintf-gnu |
vsprintf | vszprintf | vszprintf , vszprintf-posix , vszprintf-gnu |
snprintf | snzprintf | snzprintf , snzprintf-posix , snzprintf-gnu |
vsnprintf | vsnzprintf | vsnzprintf , vsnzprintf-posix , vsnzprintf-gnu |
asprintf | aszprintf | vaszprintf , vaszprintf-posix , vaszprintf-gnu |
vasprintf | vaszprintf | vaszprintf , vaszprintf-posix , vaszprintf-gnu |
c_snprintf | c_snzprintf | c-snzprintf , c-snzprintf-gnu |
c_vsnprintf | c_vsnzprintf | c-vsnzprintf , c-vsnzprintf-gnu |
c_asprintf | c_aszprintf | c-vaszprintf , c-vaszprintf-gnu |
c_vasprintf | c_vaszprintf | c-vaszprintf , c-vaszprintf-gnu |
fprintf | fzprintf | fzprintf , fzprintf-posix , fzprintf-gnu |
vfprintf | vfzprintf | vfzprintf , vfzprintf-posix , vfzprintf-gnu |
printf | zprintf | zprintf , zprintf-posix , zprintf-gnu |
vprintf | vzprintf | vzprintf , vzprintf-posix , vzprintf-gnu |
dprintf | dzprintf | dzprintf , dzprintf-posix , dzprintf-gnu |
vdprintf | vdzprintf | vdzprintf , vdzprintf-posix , vdzprintf-gnu |
obstack_printf | obstack_zprintf | obstack-zprintf ,
obstack-zprintf-posix , obstack-zprintf-gnu |
obstack_vprintf | obstack_vzprintf | obstack-zprintf ,
obstack-zprintf-posix , obstack-zprintf-gnu |
The following functions use the *zprintf
functions under the hood
and thus don’t need a *zprintf
variant:
Function | Modules |
---|---|
xasprintf | xvasprintf , xvasprintf-posix , xvasprintf-gnu |
xvasprintf | xvasprintf , xvasprintf-posix , xvasprintf-gnu |
c_xasprintf | c-xvasprintf |
c_xvasprintf | c-xvasprintf |
xprintf | xprintf , xprintf-posix , xprintf-gnu |
xvprintf | xprintf , xprintf-posix , xprintf-gnu |
xfprintf | xprintf , xprintf-posix , xprintf-gnu |
xvfprintf | xprintf , xprintf-posix , xprintf-gnu |
Note: Even with the *zprintf
functions,
you need to be prepared to handle specific error codes
when you use non-simple format strings:
EILSEQ
when
the format string takes wide strings or wide characters as arguments,
EOVERFLOW
when
the format string takes a width as argument
and you cannot ensure that its value is in the range 0
...INT_MAX
.