怎样才能打印一个size_tvariables可移植地使用printf家族?
我有一个size_t
types的variables,我想用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位计算机上编译警告?
编辑:我想一个答案可能是“投”variables为unsigned long
,并打印使用%lu
。 这在两种情况下都会起作用。 我正在查看是否有任何其他的想法。
使用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
但是显示它就好像它已经被签名一样) - 微软表示,
%Iu
(或%Ix
,或者%Id
但又是签署的等等) – 但是从cl v19开始(在Visual Studio 2015中),Microsoft支持%zu
(请参阅此评论的 回复 )
…当然,如果您使用的是C ++,您可以按照AraK的build议使用cout
。
对于C89,使用%lu
并将值转换为unsigned long
:
size_t foo; ... printf("foo = %lu\n", (unsigned long) foo);
对于C99及更高版本,请使用%zu
:
size_t foo; ... printf("foo = %zu\n", foo);
延续Adam Rosenfield对Windows的回答。
我在VS2013 Update 4和VS2015预览版上testing了这个代码:
// 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生成的那个说:
祖
ZX
ZD
注意: ssize_t
是一个POSIX扩展, SSIZE_T
在Windows数据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>
现在包含在最新的预览中。
有关更多详细信息,请参阅旧的和新的标准符合性列表。
std::size_t s = 1024; std::cout << s; // or any other kind of stream like stringstream!
对于那些在C ++中做这个不一定支持C99扩展的人来说,我衷心推荐boost :: format。 这使size_ttypes的大小问题没有争议:
std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);
既然你不需要boost :: format中的大小说明符,你可以担心你想如何显示值。
printf("size = %zu\n", sizeof(thing) );
如果您将32位无符号整数传递给%lu格式,它会警告您吗? 这应该没问题,因为转换是明确的,不会丢失任何信息。
我听说有些平台在<inttypes.h>
中定义了可插入到格式string文字中的macros,但在Windows C ++编译器中看不到该标题,这意味着它可能不是跨平台的。
正如AraK所说,c ++stream接口总是可以工作的。
std :: size_t s = 1024; std :: cout << s; //或任何其他types的stream如stringstream!
如果你想要C stdio,对于某些“便携”的情况,没有可移植的答案。 如你所见,它会变得难看,select错误的格式标志可能会产生编译器警告或给出不正确的输出。
C99试图用inttypes.h格式解决这个问题,比如“%”PRIdMAX“\ n”。 但就像“%zu”一样,并不是每个人都支持c99(比如2013之前的MSVS)。 有“msinttypes.h”文件浮动以解决这个问题。
如果您转换为其他types,则根据标志,您可能会收到编译器警告,以便截断或更改符号。 如果你走这条路线,select一个更大的相关固定大小的types。 无符号long long和“%llu”或无符号long“%lu”应该是有效的,但llu也可能在32位世界中放慢速度。 (编辑 – 即使%lu,%llu和size_t都是相同的大小,我的mac在64位中发出警告,%llu与size_t不匹配,而%lu和%llu在我的MSVS2012上大小不一样。您可能需要投射+使用匹配的格式。)
对于这个问题,你可以使用固定大小的types,比如int64_t。 可是等等! 现在我们回到c99 / c ++ 11,而旧的MSVS再次失败。 另外你也有casts(例如map.size()不是一个固定大小的types)!
您可以使用第三方头或库,例如boost。 如果你还没有使用,你可能不想这样夸大项目。 如果您愿意为此问题添加一个,为什么不使用c ++stream或条件编译?
所以你可以使用c ++stream,条件编译,第三方框架,或者某种可移植的东西来为你工作。
C99为此定义了“%zd”等。 (感谢评论者)在C ++中没有可移植的格式说明符 – 你可以使用%p
,这两种情况都可以使用word,但不是一个可移植的选项,并以hex表示。
或者,使用一些stream(例如stringstream)或安全的printfreplace(如Boost格式) 。 我明白,这个build议只是有限的使用(并且需要C ++)。 (在实现unicode支持时,我们使用了符合我们需求的类似方法。)
C的基本问题是使用省略号的printf是不安全的 – 它需要根据已知的参数来确定额外参数的大小,所以不能固定来支持“你得到的东西”。 所以,除非你的编译器实现了一些专有的扩展,否则你运气不好。
在某些平台上,对于某些types,有特定的printf转换说明符可用,但有时候必须求助于更大的types。
我已经在这里logging了这个棘手的问题,例如代码: http : //www.pixelbeat.org/programming/gcc/int_types/并定期更新它的信息在新的平台和types。
如果你想打印一个size_t的值作为一个string,你可以这样做:
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_ttypes在不同的机器上看到这个问题https://stackoverflow.com/a/918909/1755797
http://www.cplusplus.com/reference/cstdio/printf/
size_t在32位机器上是无符号整数,在64位上是unsigned long long int
但%ll总是期望一个无符号long long int。
size_t在不同的操作系统上长度不同,而%llu是相同的
#ifndef _LP64 printf( "size = %u\n", x ); #else printf( "size = %lu\n", x ); #endif
讨厌,但它的作品:)