问题 如何在`printf()`中使用“zd”说明符?


寻找有关使用的说明 "zd" 同 printf()

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

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

C规范 似乎 允许 printf("%zd\n", sz) 取决于如何阅读:

7.21.6.1 fprintf 功能

z 指定以下内容 dioux, 要么 X 转换说明符适用于a size_t 或相应的有符号整数类型参数;或者说是以下 n 转换说明符适用于指向对应的有符号整数类型的指针 size_t 论据。 C11dr§7.21.6.17

这应该被解读为

  1. z 指定以下内容 d ...转换说明符适用于a size_t 或相应的有符号整数类型参数......“(两种类型)和”z 指定以下内容 u ...转换说明符适用于a size_t 或相应的有符号整数类型参数......“(两种类型)

要么

  1. z 指定以下内容 d ...转换说明符适用于相应的有符号整数类型参数...“(仅限带符号类型)和”z 指定以下内容 u ...转换说明符适用于a size_t“(仅限无符号类型)。

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

哪个是正确的,1,2或其他什么?

如果#2是正确的,那么可以使用的类型的示例是什么 "%zd"


1567
2017-10-02 22:10


起源



答案:


printf 用一个 "%zd" format需要一个与unsigned类型对应的signed类型的参数 size_t

标准C不提供此类型的名称或确定其类型的好方法。如果 size_t 是一个typedef unsigned long例如,然后 "%zd" 期望一个类型的参数 long,但这不是一个可移植的假设。

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

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

应该工作,并应打印“42”。它将解释价值 42,无符号类型 size_t,好像它是相应的签名类型。但是,从那以后真的没有理由这样做 "%zu" 也没有诉诸其他语言规则,也是正确和明确的。和 "%zu" 效劳于 所有 类型的值 size_t,包括相应签名类型范围之外的那些。

最后,POSIX定义了一个类型 ssize_t 在标题中 <unistd.h> 和 <sys/types.h>。虽然POSIX没有明确说出来,但据推测 ssize_t 将是对应的签名类型 size_t。 因此,如果您正在编写特定于POSIX的代码, "%zd" 是(可能)打印类型的正确格式 ssize_t


14
2017-10-03 19:14



@MK:这很奇怪。它说你在2小时前编辑了我的答案(我不反对)并删除了正文中的2个字符,但我没有看到改变。 - Keith Thompson
@KeithThompson是的我必须这样做。它不允许我用投票取代downvote,因为它已经过了2个小时。这完全是愚蠢的,因为我可以编辑和改变。我问meta要删除那些有足够代表进行编辑的人的限制,但它没有去任何地方。更改是删除未显示的链接脚注之前的额外空行,因此您无法看到它(我老实说尝试了一个有意义的编辑,但答案太好:) - MK.
好的,没问题。在编辑历史中看不到任何变化,这似乎很奇怪。 - Keith Thompson
我看到了差异: imgur.com/a/QEoAS - MK.
@chux:6.2.6.2p5:“符号位为零的有符号整数类型的有效(非陷阱)对象表示是相应无符号类型的有效对象表示,并且应表示相同的值。”我只是注意到没有指定另一个方向,尽管它需要一个特殊的表示形式,具有相同的非零值的多个表示来打破对称性。 - Keith Thompson


答案:


printf 用一个 "%zd" format需要一个与unsigned类型对应的signed类型的参数 size_t

标准C不提供此类型的名称或确定其类型的好方法。如果 size_t 是一个typedef unsigned long例如,然后 "%zd" 期望一个类型的参数 long,但这不是一个可移植的假设。

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

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

应该工作,并应打印“42”。它将解释价值 42,无符号类型 size_t,好像它是相应的签名类型。但是,从那以后真的没有理由这样做 "%zu" 也没有诉诸其他语言规则,也是正确和明确的。和 "%zu" 效劳于 所有 类型的值 size_t,包括相应签名类型范围之外的那些。

最后,POSIX定义了一个类型 ssize_t 在标题中 <unistd.h> 和 <sys/types.h>。虽然POSIX没有明确说出来,但据推测 ssize_t 将是对应的签名类型 size_t。 因此,如果您正在编写特定于POSIX的代码, "%zd" 是(可能)打印类型的正确格式 ssize_t


14
2017-10-03 19:14



@MK:这很奇怪。它说你在2小时前编辑了我的答案(我不反对)并删除了正文中的2个字符,但我没有看到改变。 - Keith Thompson
@KeithThompson是的我必须这样做。它不允许我用投票取代downvote,因为它已经过了2个小时。这完全是愚蠢的,因为我可以编辑和改变。我问meta要删除那些有足够代表进行编辑的人的限制,但它没有去任何地方。更改是删除未显示的链接脚注之前的额外空行,因此您无法看到它(我老实说尝试了一个有意义的编辑,但答案太好:) - MK.
好的,没问题。在编辑历史中看不到任何变化,这似乎很奇怪。 - Keith Thompson
我看到了差异: imgur.com/a/QEoAS - MK.
@chux:6.2.6.2p5:“符号位为零的有符号整数类型的有效(非陷阱)对象表示是相应无符号类型的有效对象表示,并且应表示相同的值。”我只是注意到没有指定另一个方向,尽管它需要一个特殊的表示形式,具有相同的非零值的多个表示来打破对称性。 - Keith Thompson


根据我所做的小测试,“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;
}

0
2017-10-03 19:06



这是关于“真实”和“虚假”的? - Dietrich Epp
是对于预期的(正确的)答案是真的
感谢您的测试,但某些'“zd”始终为真'将失败 uzs 很像 SIZE_MAX。 - chux
测试是 不 确定C中的事物是否合法的正确方法 - user694733
请注意,当给定负值时,%u将始终报告其他内容(以明确定义的方式) - Phlarx