关于C#:如何使用printf系列可移植地打印size_t变量?

How can one print a size_t variable portably using the printf family?

我有一个类型为size_t的变量,我想用printf()打印它。我使用什么格式说明符可以方便地打印它?

在32位机器中,%u似乎是正确的。我用g++ -g -W -Wall -Werror -ansi -pedantic编译,没有任何警告。但当我在64位机器上编译代码时,它会产生警告。

1
2
3
4
5
6
size_t x = <something>;
printf("size = %u
"
, x );

warning: format '%u' expects type 'unsigned int',
    but argument 2 has type 'long unsigned int'

如果我把它改成%lu,警告就会消失。

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

编辑:作为一种解决方法,我想一个答案可能是将变量"强制转换"成足够大的整数,比如说unsigned long,然后使用%lu打印。这两种情况都适用。我在看是否还有其他想法。


使用z修改器:

1
2
3
4
5
6
7
8
size_t x = ...;
ssize_t y = ...;
printf("%zu
"
, x);  // prints as unsigned decimal
printf("%zx
"
, x);  // prints as hex
printf("%zd
"
, y);  // prints as signed decimal


看起来它取决于您使用的编译器(Blech)的不同:

  • GNU说%zu(或者%zx,或者%zd,但是显示的好像是签名的,等等。)
  • 微软说%Iu(或%Ix,或%Id,但还是签了名,等等);但是从cl v19(在Visual Studio 2015中)起,微软支持%zu(请参阅此评论的回复)

当然,如果你使用C++,你可以使用AraK所建议的EDCOX1 8。


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

1
2
3
4
size_t foo;
...
printf("foo = %lu
"
, (unsigned long) foo);

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

1
2
3
4
size_t foo;
...
printf("foo = %zu
"
, foo);


延伸到亚当·罗森菲尔德对Windows的回答。

我在VS2013更新4和VS2015预览版上测试了此代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// test.c

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

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

VS2015生成的二进制输出:

1
1
2

而由VS2013生成的报告说:

zu
zx
zd

注:ssize_t是posix扩展,ssize_t在windows数据类型中是类似的,所以我增加了引用。

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

1
2
3
4
5
C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>

此外,C11的现在包含在最新预览中。

有关更多详细信息,请参阅此标准一致性的新旧列表。


对于那些在C++中讨论这不一定支持C99扩展的人,我衷心地推荐Booo::格式。这使得"大小"类型的大小问题变得不重要:

1
2
std::cout << boost::format("Sizeof(Var) is %d
"
) % sizeof(Var);

因为您不需要Boost::格式的大小说明符,所以您只需担心如何显示该值。


1
2
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!


1
2
printf("size = %zu
"
, sizeof(thing) );

正如AraK所说,C++流接口总是可以移植的。

std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!

如果您想要c stdio,对于某些情况下的"可移植"就没有可移植的答案了。正如您所看到的,选择错误的格式标志可能会产生一个编译器警告或给出错误的输出。

C99试图用inttypes.h格式解决这个问题,比如"%"pridmax""。但正如"zu"一样,并非所有人都支持C99(如2013年之前的MSV)。有一些"msinttypes.h"文件可以处理这个问题。

如果强制转换为其他类型,根据标志的不同,可能会收到编译器关于截断或符号更改的警告。如果你走这条路线,选择一个更大的相关固定尺寸类型。unsigned long long和"%llu"或unsigned long"%lu"中的一个应该可以工作,但在32位的世界中,llu也可能会使速度变慢,因为它太大。(编辑-我的Mac会以64位发出一条警告,警告%llu与大小不匹配,即使%lu、%llu和大小都相同。和%lu和%llu在我的MSVS2012上的大小不同。因此,您可能需要强制转换+使用匹配的格式。)

为此,您可以使用固定大小的类型,如int64_t。但请稍候!现在我们回到C99/C++ 11,旧的MSV又失败了。此外,您还具有强制转换(例如map.size()不是固定大小类型)!

您可以使用第三方头或库,如boost。如果你还没有使用,你可能不想这样夸大你的项目。如果您愿意为这个问题添加一个,为什么不使用C++流或条件编译?

因此,你要面对C++流、条件编译、第三方框架或者一些适合你的便携式工具。


如果将32位无符号整数传递到%lu格式,它会警告您吗?它应该是好的,因为转换是定义良好的,并且不会丢失任何信息。

我听说有些平台在EDCOX1 0中定义宏,可以插入到格式字符串文字中,但是我在Windows C++编译器上没有看到这个标题,这意味着它可能不是跨平台的。


C99为此定义了"%zd"等。(感谢评论家)C++中没有可移植格式的说明符,你可以使用EDCOX1,10,这两个场景中的单词,但也不是便携式的选择,并给出了十六进制的值。

或者,使用一些流媒体(如Stringstream)或安全的printf替换,如boost格式。我理解这个建议只是有限的使用(并且确实需要C++)。(在实现Unicode支持时,我们使用了适合我们需要的类似方法。)

C的基本问题是,使用省略号的printf在设计上是不安全的-它需要根据已知的参数来确定附加参数的大小,因此不能修复它来支持"您得到的任何东西"。所以,除非您的编译器实现了一些专有扩展,否则您不走运。


在某些平台和某些类型上,有特定的printf转换说明符可用,但有时必须使用强制转换到较大的类型。

我在这里用示例代码记录了这个棘手的问题:http://www.pixelbeat.org/programming/gcc/int_类型/并定期更新新平台和类型的信息。


如果要将大小的值打印为字符串,可以执行以下操作:

1
2
3
4
5
6
7
8
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
"
,*(size_t*)&text);
printf("text: %s
"
,*(char(*)[])&line);

结果是:

编号:2337200120702199116

我们去钓鱼吧,而不是坐在我们的船上。!

编辑:由于投票数下降,重新阅读问题。我注意到他的问题不是%llu或%i64d,而是不同机器上的大小类型。请参阅此问题https://stackoverflow.com/a/918909/1755797http://www.cplusplus.com/reference/cstio/printf/

在32位机器上,大小为unsigned int,在64位机器上,大小为unsigned long int。但是%ll总是需要一个无符号长整型int。

大小在不同操作系统上的长度不同,而%llu相同