如何将 "zd" 说明符与 `printf()` 一起使用?

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

寻求有关将 "zd"printf() 一起使用的说明。

当然,以下对于 C99 及更高版本是正确的。

void print_size(size_t sz) {
  printf("%zu\n", sz);
}

C 规范 似乎 允许 printf("%zd\n", sz) 取决于它的阅读方式:

7.21.6.1 fprintf 函数

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

这个应该读作

  1. "z 指定后面的 d ... 转换说明符适用于 size_t 或相应的有符号整数类型参数 ..."(两种类型)和 " z 指定后面的 u ... 转换说明符适用于 size_t 或相应的有符号整数类型参数 ...”(两种类型)

  1. "z 指定后面的 d ... 转换说明符适用于相应的有符号整数类型参数 ..."(仅限有符号类型)和"z 指定以下 u ... 转换说明符适用于 size_t"(仅限无符号类型)。

我一直在使用 #2 定义,但现在不太确定。

Which is correct, 1, 2, or something else?

If #2 is correct, what is an example of a type that can use "%zd"?

根据我所做的小测试,"zd" 始终为真,但 "zu" 不适用于负数。

测试代码:

  #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;
}
具有 "%zd" 格式的

printf 需要一个对应于无符号类型 size_t.

的有符号类型的参数

标准 C 不提供此类型的名称或确定它是什么的好方法。例如,如果 size_tunsigned long 的 typedef,那么 "%zd" 需要一个 long 类型的参数,但这不是一个可移植的假设。

标准要求相应的有符号和无符号类型对 non-negative 值使用相同的表示,这两种类型都可以表示。一个脚注说这意味着它们可以作为函数参数互换。所以这个:

size_t s = 42;
printf("s = %zd\n", s);

应该可以,并且应该打印“42”。它会将无符号类型 size_t 的值 42 解释为对应的有符号类型。但确实没有充分的理由这样做,因为 "%zu" 也是正确且定义明确的,无需诉诸其他语言规则。并且 "%zu" 适用于类型 size_t 所有 值,包括那些超出相应符号类型范围的值。

最后,POSIX在headers<unistd.h> and <sys/types.h>中定义了一个类型ssize_t。尽管 POSIX 没有明确说明,但 ssize_t 很可能是 size_t 对应的签名类型。 因此,如果您正在编写 POSIX-specific 代码,"%zd"(可能)是打印 ssize_t.

类型值的正确格式

更新:POSIX explicitly says ssize_t 不一定是 size_t 的签名版本,所以这是不明智的编写假定它是的代码:

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 ints 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.