简体   繁体   中英

How to use "zd" specifier with `printf()`?

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

  1. " 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

  1. " 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, it's likely that 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 .

UPDATE: POSIX explicitly says that ssize_t isn't necessarily the signed version of size_t , so it's unwise to write code that assumes that it is:

ssize_t

This is intended to be a signed analog of size_t . The wording is such that an implementation may either choose to use a longer type or simply to use the signed version of the type that underlies size_t . All functions that return ssize_t ( read() and write()) describe as "implementation-defined" the result of an input exceeding { SSIZE_MAX }. It is recognized that some implementations might have int s that are smaller than size_t . A conforming application would be constrained not to perform I/O in pieces larger than { SSIZE_MAX }, but a conforming application using extensions would be able to use the full range if the implementation provided an extended range, while still having a single type-compatible interface. The symbols size_t and ssize_t are also required in <unistd.h> to minimize the changes needed for calls to read() and write() . Implementors are reminded that it must be possible to include both <sys/types.h> and <unistd.h> in the same program (in either order) without error.

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM