如何使用printf系列便携地打印size_t变量?

Aru*_*run 373 c printf

我有一个类型的变量size_t,我想用它打印printf().我使用什么格式说明符来便携地打印它?

在32位机器上,%u似乎是对的.我编译了g++ -g -W -Wall -Werror -ansi -pedantic,并没有警告.但是当我在64位机器中编译该代码时,它会产生警告.

size_t x = <something>;
printf( "size = %u\n", x );

warning: format '%u' expects type 'unsigned int', 
    but argument 2 has type 'long unsigned int'
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,警告会消失,如果我将其更改为%lu.

问题是,如何编写代码,以便在32位和64位机器上编译警告?

编辑:作为一种解决方法,我想一个答案可能是将变量"转换"为一个足够大的整数,比如说unsigned long,并使用打印%lu.这在两种情况下都有效.我在寻找是否还有其他想法.

Ada*_*eld 452

使用z修饰符:

size_t x = ...;
ssize_t y = ...;
printf("%zu\n", x);  // prints as unsigned decimal
printf("%zx\n", x);  // prints as hex
printf("%zd\n", y);  // prints as signed decimal
Run Code Online (Sandbox Code Playgroud)

  • @avakar @Adam Rosenfield @Christoph @GMan:但是,在n3035§1.2规范性引用中,仅引用了C99标准,并且相同状态的§17.6.1.2/ 3"提供了C标准库的设施".我将此解释为,除非另有说明,否则C99标准库中的_everything_是C++ 0x标准库的一部分,包括C99中的其他格式说明符. (10认同)
  • @ArunSaha:这只是C99的一个功能,而不是C++.如果你想用`-pedantic`编译它,你需要得到一个支持C++ 1x草案的编译器(极不可能),或者你需要将你的代码移动到一个编译为C99的文件中.否则,您唯一的选择是将变量转换为`unsigned long long`并使用`%llu`来最大程度地移植. (8认同)
  • +1.这是一个C99添加还是这也适用于C++(我没有C90方便)? (6认同)
  • 这是C99的补充,并没有在2009-11-09的C++ 0x草案的'printf()`长度修饰符列表中显示(第672页的表84) (5认同)
  • @Christoph:也不是最新的选秀,n3035. (2认同)
  • 您应该添加指向以下引用的链接,例如http://www.gnu.org/software/libc/manual/html_node/Integer-Conversions.html#Integer-Conversions (2认同)
  • `%zd` 确实不应该出现在该列表中。`%d` 需要一个*有符号*整数,而 `size_t` 根据定义是*无符号*。 (2认同)

T.J*_*der 85

看起来它取决于您正在使用的编译器(blech):

  • gnu说%zu(或者%zx,或者%zd显示它好像是签名等)
  • 微软称%Iu(或%Ix,或者%Id不过再次证明人签署的等) -但作为CL V19(在Visual Studio 2015年),微软支持的%zu (见此回复这条评论)

...当然,如果你使用的是C++,你可以cout按照AraK的建议使用.

  • @TJCrowder:我不认为`%zd`应该在列表中.我想不出有什么理由使用`%zd`而不是`%zu`来打印`size_t`值.如果值超过`SIZE_MAX/2`,它甚至不是有效的(具有未定义的行为).(为完整起见,你可能会提到'%zo`为八进制.) (10认同)
  • `%zd`对于`size_t`是不正确的; 对于与`size_t`对应的signed类型是正确的,但`size_t`本身是无符号类型. (7认同)
  • newlib(即cygwin)也支持`z` (3认同)
  • @FUZxxl:POSIX 不要求 `ssize_t` 是对应于 `size_t` 的有符号类型,因此不能保证匹配 `"%zd"`。(它*可能*在大多数实现中。)http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html (3认同)

Joh*_*ode 58

对于C89,使用%lu并将值转换为unsigned long:

size_t foo;
...
printf("foo = %lu\n", (unsigned long) foo);
Run Code Online (Sandbox Code Playgroud)

对于C99及更高版本,请使用%zu:

size_t foo;
...
printf("foo = %zu\n", foo);
Run Code Online (Sandbox Code Playgroud)

  • 考虑到2013年,建议"对于C99及以后"和"对于前C99:".最佳答案. (7认同)
  • 不要这样做.它将在64位Windows上失败,其中size_t为64位且long为32位. (6认同)
  • 或者:您可以转换为 `uint64_t`,然后使用 inttypes.h 中的 `PRIu64` 宏,其中包含格式说明符。 (2认同)
  • @JamesKo 这有什么意义?`uint64_t` 是 C99,所以如果它可用,那么 `"%zu"` 也是如此(这是正确的方法)。 (2认同)

Kei*_*son 11

在任何相当现代的 C 实现中,"%zu"打印类型值的正确方法是size_t

printf("sizeof (int) = %zu\n", sizeof (int));
Run Code Online (Sandbox Code Playgroud)

格式"%zu"说明符是在 1999 年 ISO C 标准中添加的(并被 2011 年 ISO C++ 标准采用)。如果您不需要关心比这更旧的实现,那么您现在可以停止阅读。

如果您的代码需要移植到 C99 之前的实现,您可以将该值转换为unsigned long并使用"%lu"

printf("sizeof (int) = %lu\n", (unsigned long)sizeof (int));
Run Code Online (Sandbox Code Playgroud)

这不能移植到 C99 或更高版本,因为 C99 引入了long long和,因此比 更广泛的unsigned long long可能性。size_tunsigned long

抵制使用"%lu""%llu"不使用石膏的诱惑。用于实现的类型size_t是实现定义的,如果类型不匹配,则行为未定义。类似的东西printf("%lu\n", sizeof (int));可能“有效”,但它根本不便携。

原则上,以下内容涵盖所有可能的情况:

#if __STDC_VERSION__ < 199901L
    printf("sizeof (int) = %lu\n", (unsigned long)sizeof (int));
#else
    printf("sizeof (int) = %zu\n", sizeof (int));
#endif
Run Code Online (Sandbox Code Playgroud)

在实践中,它可能并不总是正确工作。__STD_VERSION__ >= 199901L 应该保证它"%zu"是受支持的,但并非所有实现都一定是正确的,特别是因为__STD_VERSION__它是由编译器设置并由"%zu"运行时库实现的。例如,具有部分C99 支持的实现可能会实现long long并创建 的size_ttypedef unsigned long long,但不支持"%zu"。(这样的实现可能不会定义__STDC_VERSION__。)

有人指出微软的实现可以有32位unsigned long和64位size_t. Microsoft 确实支持"%zu",但该支持添加得相对较晚。另一方面,只有当特定值碰巧超过 时,转换为unsigned long才会出现问题,而这在实践中不太可能发生。size_tULONG_MAX

如果您能够假设相当现代的实现,只需使用"%zu". 如果您需要允许较旧的实现,这里有一个可适应各种配置的可移植程序:

#include <stdio.h>
#include <limits.h>
int main(void) {
    const size_t size = -1; /* largest value of type size_t */
#if __STDC_VERSION__ < 199901L
    if (size > ULONG_MAX) {
        printf("size is too big to print\n");
    }
    else {
        printf("old: size = %lu\n", (unsigned long)size);
    }
#else
    printf("new: size = %zu\n", size);
#endif
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

一个打印“尺寸太大而无法打印”的实现(x86_64-w64-mingw32-gcc.exe -std=c90在 Windows/Cygwin 上)实际上支持unsigned long long作为 C90 之上的扩展,因此您也许可以利用它 - 但我可以想象 C99 之前的实现支持unsigned long long但不支持"%llu"。无论如何,该实现都支持"%zu"

size_t根据我的经验,当我探索实现时,我只想在快速一次性代码中打印值,而不是在生产代码中打印值。在这种情况下,做任何有效的事情可能就足够了。

(问题是关于 C 的,但我会提到 C++std::cout << sizeof (int)可以在该语言的任何版本中正常工作。)


vul*_*ven 9

扩展Adam Rosenfield对Windows的回答.

我在VS2013 Update 4和VS2015预览版上测试了这段代码:

// test.c

#include <stdio.h>
#include <BaseTsd.h> // see the note below

int main()
{
    size_t x = 1;
    SSIZE_T y = 2;
    printf("%zu\n", x);  // prints as unsigned decimal
    printf("%zx\n", x);  // prints as hex
    printf("%zd\n", y);  // prints as signed decimal
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

VS2015生成二进制输出:

1
1
2

而由VS2013生成的那个说:

zu
zx
zd

注意:ssize_t是一个POSIX扩展,SSIZE_TWindows数据类型中类似,因此我添加了<BaseTsd.h>参考.

此外,除了以下C99/C11标头外,所有C99标头都可在VS2015预览中使用:

C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>
Run Code Online (Sandbox Code Playgroud)

此外,C11 <uchar.h>现已包含在最新预览中.

有关详细信息,请参阅本旧的新的标准符合性列表.


nat*_*ose 5

printf("size = %zu\n", sizeof(thing) );
Run Code Online (Sandbox Code Playgroud)


Ara*_*raK 5

std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!
Run Code Online (Sandbox Code Playgroud)

  • @AraK.他们很慢?他们添加大量字节的原因不多.ArunSaha只是想知道他/她自己的个人知识?个人喜好(我更喜欢stdio来自己).原因很多. (9认同)
  • 是的,但提问者专门询问了'printf`说明符.我猜他们有一些其他未说明的约束使得使用`std :: cout`成为一个问题. (8认同)
  • @TKCrowder:嗯,最初的请求确实说需要一个 C 解决方案(通过标记),并且有充分的理由不在 C++ 中使用流,例如,如果从消息目录中提取输出格式描述符。(如果需要,您可以为消息编写一个解析器并使用流,但是当您只能利用现有代码时,这会是一项繁重的工作。) (2认同)

swe*_*rup 5

对于那些谈论用C++做这个并不一定支持C99扩展的人,我衷心推荐boost :: format.这使得size_t类型大小问题没有实际意义:

std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);
Run Code Online (Sandbox Code Playgroud)

由于您不需要boost :: format中的大小说明符,因此您可以担心如何显示值.

  • 可能想要'%u`然后. (3认同)