Looking for clarification on using "zd"
with printf()
.
Certainly the following is correct with C99 and later.
void print_size(size_t sz) {
printf("%zu\n", sz);
}
The C spec seems to allow printf("%zd\n", sz)
depending on how it is read:
7.21.6.1 The fprintf
function
z
Specifies that a following d
, i
, o
, u
, x
, or X
conversion specifier applies to a size_t
or the corresponding signed integer type argument; or that a following n
conversion specifier applies to a pointer to a signed integer type corresponding to size_t
argument. C11dr §7.21.6.1 7
Should this be read as
- "
z
Specifies that a following d
... conversion specifier applies to a size_t
or the corresponding signed integer type argument ... "(both types) and "z
Specifies that a following u
... conversion specifier applies to a size_t
or the corresponding signed integer type argument ..." (both types)
OR
- "
z
Specifies that a following d
... conversion specifier applies to a corresponding signed integer type argument ..." (signed type only) and "z
Specifies that a following u
... conversion specifier applies to a size_t
" (unsigned type only).
I've been using the #2 definition, but now not so sure.
Which is correct, 1, 2, or something else?
If #2 is correct, what is an example of a type that can use "%zd"
?
printf
with a "%zd"
format expects an argument of the signed type that corresponds to the unsigned type size_t
.
Standard C doesn't provide a name for this type or a good way to determine what it is. If size_t
is a typedef for unsigned long
, for example, then "%zd"
expects an argument of type long
, but that's not a portable assumption.
The standard requires that corresponding signed and unsigned types use the same representation for the non-negative values that are representable in both types. A footnote says that this is meant to imply that they're interchangeable as function arguments. So this:
size_t s = 42;
printf("s = %zd\n", s);
should work, and should print "42". It will interpret the value 42
, of the unsigned type size_t
, as if it were of the corresponding signed type. But there's really no good reason to do that, since "%zu"
is also correct and well defined, without resorting to additional language rules. And "%zu"
works for all values of type size_t
, including those outside the range of the corresponding signed type.
Finally, POSIX defines a type ssize_t
in the headers <unistd.h>
and <sys/types.h>
. Though POSIX doesn't explicitly say so, presumably ssize_t
will be the signed type corresponding to size_t
.
So if you're writing POSIX-specific code, "%zd"
is (probably) the correct format for printing values of type ssize_t
.
According to the little test I have done, "zd" is always true ,but "zu" don't work for negative numbers.
Test Code:
#include <stdio.h>
int main (void)
{ int i;
size_t uzs = 1;
ssize_t zs = -1;
for ( i= 0; i<5 ;i++, uzs <<= 16,zs <<= 16 )
{
printf ("%zu\t", uzs); /*true*/
printf ("%zd\t", uzs); /*true*/
printf ("%zu\t", zs); /* false*/
printf ("%zd\n", zs); /*true*/
}
return 0;
}