如何使用printf系列可移植地打印size_t变量?


403

我有一个类型的变量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。在两种情况下都可以。我在寻找是否还有其他想法。


4
unsigned long如果您的libc实现不支持z修饰符,则强制转换为最佳选择。C99标准建议size_t不要有一个整数转换等级大于long,那么你是相当安全的
克里斯托夫



1
在Windows平台上,size_t可以大于long。出于兼容性原因,long始终为32位,但size_t可以为64位。因此,强制转换为无符号长整数可能会丢失一半的位。抱歉:-)
布鲁斯·道森

Answers:


481

使用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

7
+1。这是C99的补充,还是也适用于C ++(我没有C90)?
阿瓦卡

6
这是C99的附加内容,未出现在printf()2009
Christoph

3
@Christoph:在最新的草案n3035中也没有。
GManNickG 2010年

11
@avakar @Adam Rosenfield @Christoph @GMan:但是,在n3035§1.2规范性引用中,仅引用了C99标准,并且§17.6.1.2/ 3具有相同的状态“提供了C标准库的功能”。我的解释是,除非另有说明,否则C99标准库中的所有内容都是 C ++ 0x标准库的一部分,包括C99中的其他格式说明符。
James McNellis

9
@ArunSaha:这仅是C99的功能,而不是C ++的功能。如果要使用进行编译-pedantic,则需要获得支持C ++ 1x草案的编译器(极不可能),或者需要将代码移动到以C99编译的文件中。否则,您唯一的选择是将变量强制转换为最大可移植性unsigned long long并使用%llu
亚当·罗森菲尔德

88

看起来这取决于您所使用的编译器(故障):

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

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


3
z也被newlib支持(即cygwin的)
克里斯托夫

7
%zd不正确的size_t; 对应于的有符号类型是正确的size_t,但size_t本身是无符号类型。
基思·汤普森

1
@KeithThompson:我也提到%zu过(%zx以防他们想要十六进制)。足够真实,%zu应该应该在列表中排在第一位。固定。
TJ Crowder

10
@TJCrowder:我认为根本不%zd应该在列表中。我想不出任何理由使用%zd而不是%zu打印一个size_t值。如果该值超过,则甚至无效(具有未定义的行为)SIZE_MAX / 2。(为了完整起见,你可能会提到%zo的八进制)
基思·汤普森

2
@FUZxxl:POSIX不需要ssize_t是与相对应的带符号类型size_t,因此不能保证匹配"%zd"。(这可能是大多数的实现。)pubs.opengroup.org/onlinepubs/9699919799/basedefs/...
基思·汤普森

59

对于C89,使用%lu并将其强制转换为unsigned long

size_t foo;
...
printf("foo = %lu\n", (unsigned long) foo);

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

size_t foo;
...
printf("foo = %zu\n", foo);

7
考虑到2013年,建议“对于C99及更高版本”和“对于C99之前的版本:”。最佳答案。
chux-恢复莫妮卡

8
不要这样做。在size_t为64位而long为32位的64位Windows上,它将失败。
Yttrill

1
@Yttrill:那么64位Windows的答案是什么?
约翰·博德

1
@JohnBode也许unsigned long long吗?
詹姆斯·柯

2
或者:您可以转换为a uint64_t,然后使用PRIu64inttypes.h中的宏,其中包含格式说明符。
詹姆斯·柯

9

扩展了Windows的Adam Rosenfield的答案。

我在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_TWindows数据类型中是类似的东西,因此我添加了<BaseTsd.h>参考。

此外,除了以下C99 / C11标头外,VS2015预览版中还提供了所有C99标头:

C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>

而且,C11 <uchar.h>现在已包含在最新预览中。

有关更多详细信息,请参见列表和列表以了解标准一致性。


VS2013 Update 5产生与Update 4相同的结果。
内森·基德

6

对于那些谈论用C ++不一定支持C99扩展的人,我衷心推荐boost :: format。这使得size_t类型大小问题无济于事:

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

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


4
大概想要吧%u
GManNickG

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

8
是的,但发问者专门要求printf说明者。我猜想他们还有其他一些未说明的约束会导致使用std::cout问题。
Donal Fellows 2010年

1
@Donal我想知道C ++流会在C ++项目中造成什么样的问题!
AraK

9
@AraK。他们很慢吗?他们添加了很多字节,原因并不多。ArunSaha只是想了解自己的个人知识?个人喜好(我更喜欢stdio而不是fstream自己)。原因有很多。
KitsuneYMG 2010年

1
@TKCrowder:好吧,最初的请求确实要求使用C解决方案(通过标记),并且有充分的理由不使用C ++中的流,例如,如果从消息目录中提取了输出格式描述符。(如果需要,您可以编写消息解析器并使用流,但这仅需要利用现有代码即可完成很多工作。)
Donal Fellows 2010年

1
@Donal:标签是C和C ++。我绝不主张C ++的I / O流东西(我不喜欢它),只是指出该问题最初不是 *“ ...向说明者索要规范printf”。
TJ Crowder 2010年

5
printf("size = %zu\n", sizeof(thing) );

2

正如AraK所说,c ++流接口将始终可移植地工作。

std :: size_t s = 1024; std :: cout << s; //或其他任何类型的流,例如stringstream!

如果您要使用C stdio,则在某些“便携式”情况下,对此没有可移植的答案。如您所见,这变得很丑陋,选择错误的格式标志可能会产生编译器警告或给出不正确的输出。

C99尝试使用inttypes.h格式(例如“%” PRIdMAX” \ n“)解决此问题。但是就像“%zu”一样,并不是每个人都支持c99(例如2013年之前的MSVS)。有“ msinttypes.h”文件在附近处理。

如果强制转换为其他类型,则根据标志,您可能会收到编译器警告,要求其截断或更改符号。如果您选择此路线,请选择较大的相关固定尺寸类型。无符号长整型和“%llu”或无符号长整型“%lu”之一应该可以工作,但是在32bit环境中,llu可能会因为过大而使速度变慢。(编辑-尽管%lu,%llu和size_t大小相同,但我的Mac仍以%llu不匹配size_t的方式发出了64位警告。MSVS2012上%lu和%llu的大小也不相同。因此您可能需要强制转换+使用匹配的格式。)

为此,您可以使用固定大小的类型,例如int64_t。可是等等!现在我们回到c99 / c ++ 11,旧的MSVS再次失败。另外,您还具有强制类型转换(例如map.size()不是固定大小的类型)!

您可以使用第三方标头或库,例如boost。如果您尚未使用它,则可能不希望以这种方式为您的项目充气。如果您愿意为此添加一个,为什么不使用c ++流或条件编译呢?

因此,您只能使用c ++流,条件编译,第三者框架或某种可为您工作的便携式工具。


-1

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

我听说某些平台定义宏,<inttypes.h>因为您可以将其插入格式字符串文字中,但我在Windows C ++编译器上看不到该标头,这意味着它可能不是跨平台的。


1
如果将错误大小的内容传递给printf,大多数编译器都不会警告您。GCC是一个例外。inttypes.h是在C99中定义的,因此任何符合C99的C编译器都将拥有它,而现在应该全部使用它。不过,您可能必须使用编译器标志打开C99。无论如何,intttypes.h不会为size_t或ptrdiff_t定义特定的格式,因为它们被确定为足够重要,足以分别获得其“ z”和“ t”的大小指定符。
swestrup

如果使用%lu,则应将size_t值强制转换为unsigned long。的参数没有隐式转换(除了提升)printf
基思·汤普森

-2

C99为此定义了“%zd”等。(感谢评论者)在C ++中没有可移植的格式说明符-您可以使用%p,这在这两种情况下都会用到单词,但也不是可移植的选择,并且以十六进制给出值。

或者,使用某些流式传输(例如stringstream)或安全的printf替代品,例如Boost Format。我了解此建议仅用途有限(并且确实需要C ++)。(在实现unicode支持时,我们使用了适合我们需求的类似方法。)

C语言的基本问题是,使用省略号的printf在设计上是不安全的-它需要根据已知参数确定附加参数的大小,因此无法固定为支持“无论您得到什么”。因此,除非您的编译器实现某些专有扩展,否则您将很不走运。


2
z大小modidfier是标准的C,但有些libc的实现是卡在1990年因各种原因(如Microsoft支持的C基本放弃了C ++和-最近- C#)
克里斯托夫

3
C99将大小说明符“ z”定义为size_t值的大小,将“ t”定义为ptrdiff_t值的大小。
swestrup

2
%zd是错误的,因此未签名%zu
戴维·康拉德

-3

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

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


1
请注意,不鼓励仅链接的答案,因此,SO答案应该是搜索解决方案的终点(与引用的另一种中途停留相比,随着时间的流逝,它们会变得陈旧)。请考虑在此处添加独立的摘要,并保留该链接作为参考。
kleopatra

-5

如果要将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。

在%llu相同的情况下,size_t的长度在不同的操作系统上会有所不同


4
这是什么废话?
安蒂·哈帕拉

我通过size_t指针将char数组的前8个字节强制转换为一个无符号的长long 64bit,并使用printf%I64d将它们打印为数字,这并不是很引人注目,我当然不知道防止代码溢出的代码,但是那不在问题的范围之内。
安德烈(Andre)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.