我有一个 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'
如果我将其更改为 %lu
,则警告会按预期消失。
问题是,如何编写代码,以便在 32 位和 64 位机器上编译无警告?
编辑:作为一种解决方法,我想一个答案可能是将变量“转换”为一个足够大的整数,例如 unsigned long
,然后使用 %lu
打印。这在这两种情况下都有效。我正在寻找是否有任何其他想法。
z
修饰符,则强制转换为 unsigned long
是最佳选择; C99 标准建议 size_t
的整数转换等级不要大于 long
,因此您是相当安全的
使用 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
看起来它取决于您使用的编译器(blech):
gnu 说 %zu (或 %zx 或 %zd 但显示它好像它已签名等)
Microsoft 说 %Iu(或 %Ix,或 %Id,但又是已签名的,等等)——但从 cl v19(在 Visual Studio 2015 中)开始,Microsoft 支持 %zu(请参阅此评论的回复)
...当然,如果您使用 C++,您可以使用 cout
代替 suggested by AraK。
z
size_t
的 %zd
不正确; size_t
对应的有符号类型是正确的,但 size_t
本身是无符号类型。
%zu
(以及 %zx
以防他们想要十六进制)。确实如此,%zu
可能应该是列表中的第一个。固定的。
%zd
根本不应该在列表中。我想不出任何理由使用 %zd
而不是 %zu
来打印 size_t
值。如果值超过 SIZE_MAX / 2
,它甚至无效(具有未定义的行为)。 (为了完整起见,您可能会提到 %zo
表示八进制。)
ssize_t
是对应于 size_t
的签名类型,因此不能保证匹配 "%zd"
。 (它可能在大多数实现中。)pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
对于 C89,使用 %lu
并将值强制转换为 unsigned long
:
size_t foo;
...
printf("foo = %lu\n", (unsigned long) foo);
对于 C99 及更高版本,请使用 %zu
:
size_t foo;
...
printf("foo = %zu\n", foo);
uint64_t
,然后使用 inttypes.h 中的 PRIu64
宏,其中包含格式说明符。
uint64_t
是 C99,所以如果它可用,那么 "%zu"
也是如此(这是正确的做法)。
扩展 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;
}
VS2015 生成的二进制输出:
1 1 2
而VS2013生成的那个说:
zu zx zd
注意:ssize_t
是 POSIX 扩展,SSIZE_T
在 Windows Data Types 中是类似的东西,因此我添加了 <BaseTsd.h>
引用。
此外,除了以下 C99/C11 头文件外,所有 C99 头文件都在 VS2015 预览版中可用:
C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>
此外,C11 的 <uchar.h>
现在包含在最新预览版中。
有关更多详细信息,请参阅此 old 和 new 列表以了解标准一致性。
printf("size = %zu\n", sizeof(thing) );
对于那些谈论在不一定支持 C99 扩展的 C++ 中执行此操作的人,我衷心推荐 boost::format。这使得 size_t 类型大小问题变得毫无意义:
std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);
由于您不需要 boost::format 中的大小说明符,因此您只需担心要如何显示该值。
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!
printf
说明符。我猜他们还有其他一些未说明的限制,这使得使用 std::cout
成为问题。
printf
说明符。”
在任何合理的现代 C 实现中,"%zu"
是打印 size_t
类型值的正确方法:
printf("sizeof (int) = %zu\n", sizeof (int));
"%zu"
格式说明符已添加到 1999 ISO C 标准中(并被 2011 ISO C++ 标准采用)。如果您不需要关心比这更早的实现,您现在可以停止阅读。
如果您的代码需要移植到 C99 之前的实现,您可以将值强制转换为 unsigned long
并使用 "%lu"
:
printf("sizeof (int) = %lu\n", (unsigned long)sizeof (int));
这不能移植到 C99 或更高版本,因为 C99 引入了 long long
和 unsigned long long
,因此 size_t
可能比 unsigned 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
在实践中,它可能并不总是正常工作。 __STD_VERSION__ >= 199901L
应该保证支持 "%zu"
,但并非所有实现都一定正确,尤其是因为 __STD_VERSION__
由编译器设置,而 "%zu"
由运行时库实现。例如,具有部分 C99 支持的实现可能会实现 long long
并使 size_t
成为 unsigned long long
的 typedef,但不支持 "%zu"
。 (这样的实现可能不会定义 __STDC_VERSION__
。)
有人指出,微软的实现可以有 32 位 unsigned long
和 64 位 size_t
。 Microsoft 确实支持 "%zu"
,但该支持是相对较晚添加的。另一方面,只有当特定的 size_t
值恰好超过 ULONG_MAX
时,强制转换为 unsigned long
才会成为问题,这在实践中不太可能发生。
如果您能够假设相当现代的实现,只需使用 "%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;
}
一种打印“尺寸太大而无法打印”的实现(Windows/Cygwin 上的 x86_64-w64-mingw32-gcc.exe -std=c90
)实际上支持 unsigned long long
作为 C90 之上的扩展,因此您也许可以利用它——但我可以想象支持 unsigned long long
但不支持 "%llu"
的 C99 之前的实现。无论如何,该实现都支持 "%zu"
。
根据我的经验,当我在探索实现而不是在生产代码中时,我只想在快速一次性代码中打印 size_t
值。在这种情况下,做任何有效的事情可能就足够了。
(问题是关于 C 的,但我会提到在 C++ 中 std::cout << sizeof (int)
可以在任何版本的语言中正常工作。)
正如 AraK 所说,c++ 流接口将始终可移植地工作。
std::size_t s = 1024; std::cout << s; // 或任何其他类型的流,如 stringstream!
如果你想要 C stdio,对于某些“便携”的情况,没有便携的答案。而且它变得丑陋,因为正如您所见,选择错误的格式标志可能会产生编译器警告或给出不正确的输出。
C99 尝试使用诸如“%”PRIdMAX“\n”之类的 inttypes.h 格式来解决这个问题。但就像“%zu”一样,并不是每个人都支持 c99(就像 2013 年之前的 MSVS)。有“msinttypes.h”文件来处理这个问题。
如果您转换为不同的类型,根据标志,您可能会收到关于截断或符号更改的编译器警告。如果您走这条路线,请选择更大的相关固定尺寸类型。 unsigned long long 和 "%llu" 或 unsigned long "%lu" 之一应该可以工作,但 llu 在 32 位世界中也可能会减慢速度,因为它太大了。 (编辑 - 我的 mac 在 64 位中针对 %llu 与 size_t 不匹配发出警告,即使 %lu、%llu 和 size_t 的大小都相同。而且我的 MSVS2012 上的 %lu 和 %llu 大小不同。所以您可能需要强制转换 + 使用匹配的格式。)
就此而言,您可以使用固定大小的类型,例如 int64_t。可是等等!现在我们回到 c99/c++11,旧的 MSVS 再次失败。另外,您还有演员表(例如 map.size() 不是固定大小的类型)!
您可以使用 3rd 方标头或库,例如 boost。如果您还没有使用一个,您可能不想以这种方式夸大您的项目。如果您愿意为这个问题添加一个,为什么不使用 c++ 流或条件编译呢?
因此,您可以使用 c++ 流、条件编译、第 3 方框架,或者碰巧适合您的某种可移植的东西。
在程序员想要输出 size_t
的大多数情况下,程序员会对输出的数值有一个合理的上限。例如,如果程序员正在输出一条消息,说明 int
有多大,请使用:
printf("int is %u bytes", (unsigned)sizeof (int) );
出于所有实际目的,与以下一样便携,但可能更快更小:
printf("int is %zu bytes", sizeof (int) );
这种构造可能失败的唯一情况是在一个平台上,其中 int
上值得填充的字节数相对于 unsigned int
可以表示的最大值的幅度大得离谱({ 3} 可能大于 65535,但更令人难以置信的是,如果 unsigned
没有足够的值位来表示大于 sizeof (int)
的数字,它可能会那么大。
如果您将 32 位无符号整数传递给 %lu 格式,它会警告您吗?应该没问题,因为转换定义明确并且不会丢失任何信息。
我听说某些平台在 <inttypes.h>
中定义了可以插入格式字符串文字的宏,但我在 Windows C++ 编译器上看不到该标头,这意味着它可能不是跨平台的。
%lu
,则应将 size_t
值强制转换为 unsigned long
。 printf
的参数没有隐式转换(促销除外)。
C99 为此定义了“%zd”等。 (感谢评论者)C++ 中没有可移植的格式说明符 - 您可以使用 %p
,这在这两种情况下都可以使用,但也不是可移植的选择,并给出十六进制值。
或者,使用一些流式传输(例如 stringstream)或安全的 printf 替换,例如 Boost Format。我了解此建议的用途有限(并且确实需要 C++)。 (在实现 unicode 支持时,我们使用了适合我们需求的类似方法。)
C 的基本问题是使用省略号的 printf 在设计上是不安全的——它需要从已知参数中确定附加参数的大小,因此无法修复它以支持“无论你得到什么”。所以除非你的编译器实现了一些专有的扩展,否则你就不走运了。
z
大小修饰符是标准 C,但由于各种原因,一些 libc 实现在 1990 年停滞不前(例如,Microsoft 基本上放弃了 C 以支持 C++ 和 - 最近 - C#)
%zd
是错误的,它是无符号的,所以它应该是 %zu
。
在某些平台和某些类型上,有特定的 printf 转换说明符可用,但有时不得不求助于转换为更大的类型。
我在这里记录了这个棘手的问题,示例代码:http://www.pixelbeat.org/programming/gcc/int_types/ 并定期更新它,提供有关新平台和类型的信息。
如果要将 size_t 的值打印为字符串,可以执行以下操作:
char text[] = "Lets go fishing in stead of sitting on our but !!";
size_t line = 2337200120702199116;
/* on windows I64x or I64d others %lld or %llx if it works %zd or %zx */
printf("number: %I64d\n",*(size_t*)&text);
printf("text: %s\n",*(char(*)[])&line);
结果是:
电话:2337200120702199116
文字:让我们去钓鱼而不是坐在我们的但是!
编辑:重读这个问题,因为我注意到他的问题不是 %llu 或 %I64d 而是不同机器上的 size_t 类型看到这个问题 https://stackoverflow.com/a/918909/1755797
http://www.cplusplus.com/reference/cstdio/printf/
size_t 在 32 位机器上是 unsigned int,在 64 位机器上是 unsigned long long int,但 %ll 总是期望 unsigned long long int。
size_t 在不同的操作系统上长度不同,而 %llu 相同
不定期副业成功案例分享
printf()
长度修饰符列表中(第 672 页上的表 84)-pedantic
进行编译,您需要获得支持 C++1x 草案的编译器(极不可能),或者您需要将代码移动到编译为 C99 的文件中。否则,您唯一的选择是将变量转换为unsigned long long
并使用%llu
以最大限度地实现可移植性。