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
打印。在两种情况下都可行。我在寻找是否还有其他想法。#1 楼
使用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
评论
+1。这是C99的补充,还是也适用于C ++(我没有方便的C90)?
– avakar
2010-3-26在16:20
这是C99的附加内容,未出现在2009-11-09的C ++ 0x草案的printf()长度修饰符列表中(第672页的表84)
– Christophh
10 Mar 26 '10在16:28
@Christoph:在最新的草案n3035中也没有。
– GManNickG
2010-3-26在16:48
@avakar @Adam Rosenfield @Christoph @GMan:但是,在n3035§1.2规范性引用中,仅引用了C99标准,并且§17.6.1.2/ 3具有相同的状态“提供了C标准库的功能”。我的解释是,除非另有说明,否则C99标准库中的所有内容都是C ++ 0x标准库的一部分,包括C99中的其他格式说明符。
–詹姆斯·麦克奈利斯
10 Mar 28 '10在2:49
@ArunSaha:这仅是C99的功能,而不是C ++的功能。如果要使用-pedantic进行编译,则需要获得支持C ++ 1x草案的编译器(极不可能),或者需要将代码移至以C99编译的文件中。否则,您唯一的选择是将变量强制转换为unsigned long long,并使用%llu进行最大程度的移植。
–亚当·罗森菲尔德
2010年4月13日在1:52
#2 楼
看起来它会因您使用的编译器而不同(出现问题):gnu表示
%zu
(或%zx
或%zd
,但显示为已签名) ,等等。)Microsoft说%Iu
(或%Ix
或%Id
,但再次签名,等等)。但是从cl v19开始(在Visual Studio 2015中),Microsoft支持%zu
(请参见此注释)...当然,如果您使用的是C ++,则可以使用Q4312079q代替AraK的建议。
评论
z也受到newlib(即cygwin)的支持
– Christophh
10 Mar 26 '10在16:36
%zd对于size_t不正确;对应于size_t的有符号类型是正确的,但是size_t本身是无符号类型。
–基思·汤普森(Keith Thompson)
13年5月1日凌晨0:14
@KeithThompson:我也提到了%zu(如果需要十六进制,还提到%zx)。确实如此,%zu可能应该在列表中排在第一位。固定。
– T.J.拥挤者
13年5月1日在11:32
@ T.J。Crowder:我认为%zd根本不在列表中。我想不出任何理由使用%zd而不是%zu来打印size_t值。如果该值超过SIZE_MAX / 2,则甚至无效(具有未定义的行为)。(为完整性起见,您可能会用%zo来表示八进制。)
–基思·汤普森(Keith Thompson)
2013年5月1日14:49
@FUZxxl:POSIX不需要ssize_t是与size_t对应的带符号类型,因此不保证匹配“%zd”。 (可能在大多数实现中都是这样。)pubs.opengroup.org/onlinepubs/9699919799/basedefs / ...
–基思·汤普森(Keith Thompson)
16年5月13日在15:04
#3 楼
对于C89,请使用%lu
并将值强制转换为unsigned long
:size_t foo;
...
printf("foo = %lu\n", (unsigned long) foo);
对于C99和更高版本,请使用
%zu
:size_t foo;
...
printf("foo = %zu\n", foo);
评论
考虑到2013年,建议“对于C99及更高版本”和“对于C99之前的版本:”。最佳答案。
–chux-恢复莫妮卡
13年8月10日在5:52
不要这样做。在size_t为64位而long为32位的64位Windows上,它将失败。
– Yttrill
2015年10月10日在6:08
@Yttrill:那么64位Windows的答案是什么?
–约翰·博德
15年12月16日在12:28
@JohnBode也许长久没有签名?
–James Ko
16-09-2在16:40
或者:您可以强制转换为uint64_t,然后使用inttypes.h中的PRIu64宏,其中包含格式说明符。
–James Ko
16年9月2日在16:50
#4 楼
我在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数据类型中的类似内容,因此我添加了<BaseTsd.h>
参考。此外,除以下C99 / C11标头外,所有C99标头均在VS2015预览版中可用:
C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>
此外, C11的
<uchar.h>
现在已包含在最新预览中。有关更多详细信息,请参阅此旧列表和新列表以了解标准一致性。
评论
VS2013 Update 5产生的结果与Update 4给您的结果相同。
–内森·基德(Nathan Kidd)
17年4月5日在20:44
#5 楼
std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!
评论
是的,但是发问者专门要求一个printf说明符。我猜他们还有其他一些未说明的约束,这些约束使使用std :: cout成为问题。
–研究员
2010-3-26在16:14
@Donal我想知道C ++流会在C ++项目中造成什么样的问题!
–阿拉克
2010-3-26在16:38
@AraK。他们很慢吗?他们添加了很多字节,原因并不多。 ArunSaha只是想了解自己的个人知识?个人喜好(我更喜欢stdio而不是fstream自己)。原因有很多。
– KitsuneYMG
2010-3-26在16:58
@ T.K。Crowder:好吧,最初的请求确实说需要C解决方案(通过标记),并且有充分的理由不使用C ++中的流,例如,如果从消息目录中提取了输出格式描述符。 (如果需要,您可以编写消息解析器并使用流,但这仅需利用现有代码即可完成很多工作。)
–研究员
10 Mar 26 '10在22:41
@Donal:标签是C和C ++。我绝不主张C ++的I / O流东西(我不喜欢它),只是指出该问题最初不是*“ ...向printf说明符询问规范”。
– T.J.拥挤者
2010-3-27在6:33
#6 楼
对于那些谈论用C ++不一定支持C99扩展的人,我衷心推荐boost :: format。这使size_t类型的大小问题无济于事:std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);
由于在boost :: format中不需要大小说明符,因此您只需要担心如何显示即可值。
评论
然后可能想要%u。
– GManNickG
2010-3-26在16:47
#7 楼
printf("size = %zu\n", sizeof(thing) );
#8 楼
正如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不匹配sizell发出警告,即使%lu和%llu在我的MSVS2012上大小也不相同。所以您可能需要强制转换+使用匹配的格式。)
为此,您可以使用固定大小的类型,例如int64_t。可是等等!现在我们回到c99 / c ++ 11,旧的MSVS再次失败。另外,您还可以进行强制类型转换(例如map.size()不是固定大小的类型)!
您可以使用3rd party标头或库,例如boost。如果您尚未使用它,则可能不希望以这种方式膨胀您的项目。如果您愿意为此添加一个,为什么不使用c ++流或条件编译呢?
因此,您可以使用c ++流,条件编译,第三方框架或恰好适合您的可移植方式。
#9 楼
如果将32位无符号整数传递为%lu格式,会发出警告吗?应该很好,因为转换是定义良好的,并且不会丢失任何信息。我听说有些平台在
<inttypes.h>
中定义了宏,您可以将其插入格式字符串文字中,但是我没有在我的Windows C ++编译器上没有看到该标头,这意味着它可能不是跨平台的。评论
如果将错误大小的内容传递给printf,大多数编译器都不会警告您。 GCC是一个例外。 inttypes.h是在C99中定义的,因此任何符合C99的C编译器都将拥有它,而现在应该全部使用它。不过,您可能必须使用编译器标志打开C99。无论如何,intttypes.h不会为size_t或ptrdiff_t定义特定格式,因为它们被确定为足够重要,足以分别获得其“ z”和“ t”的大小指定符。
–westrup
10 Mar 26 '10在16:22
如果使用%lu,则应将size_t值强制转换为unsigned long。对于printf的参数,没有隐式转换(除了提升)。
–基思·汤普森(Keith Thompson)
13年5月1日凌晨0:15
#10 楼
C99为此定义了“%zd”等。 (感谢评论者)在C ++中没有可移植的格式说明符-您可以使用%p
,它在这两种情况下都会用到单词,但也不是可移植的选择,并且以十六进制给出值。或者,使用某些流式传输(例如stringstream)或安全的printf替代品,例如Boost Format。我了解此建议仅用途有限(并且确实需要C ++)。 (实现unicode支持时,我们已经使用了类似的方法来满足我们的需求。)
C的基本问题是,使用省略号的printf在设计上是不安全的-它需要确定附加参数的大小根据已知的参数,因此无法固定为支持“无论您得到什么”。因此,除非您的编译器实现某些专有扩展,否则您将很不走运。
评论
z size修改器是标准C语言,但是一些libc实现在1990年由于各种原因而停滞不前(例如,Microsoft基本上放弃了C语言,转而使用C ++,并且-最近是C#)
– Christophh
2010-3-26在16:16
C99将大小说明符“ z”定义为size_t值的大小,将“ t”定义为ptrdiff_t值的大小。
–westrup
2010-3-26在16:24
%zd错误,未签名,因此应为%zu。
–大卫·康拉德(David Conrad)
2014年10月21日19:50
#11 楼
在某些平台和某些类型上,可以使用特定的printf转换说明符,但有时必须诉诸于较大的类型。我在此处用示例代码记录了这个棘手的问题:
http://www.pixelbeat.org/programming/gcc/int_types/
并定期更新有关新平台和类型的信息。
评论
请注意,不鼓励仅链接的答案,因此,SO答案应该是搜索解决方案的终点(与引用的另一种中途停留相比,随着时间的流逝,它们往往会过时)。请考虑在此处添加独立的摘要,并保留该链接作为参考。
– kleopatra
13年7月23日在9:34
#12 楼
如果要将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 /1755797http://www.cplusplus.com/reference/cstdio/printf/
size_t在32位计算机上为unsigned int,在64位计算机上为unsigned long long int,但%ll始终期望unsigned long long int。
size_t的长度在不同的操作系统上有所不同,而%llu是相同的
评论
这是什么废话?
–安蒂·哈帕拉(Antti Haapala)
17年5月28日在19:17
我通过size_t指针将char数组的前8个字节强制转换为一个无符号的长long 64bit,并使用printf%I64d将它们打印为数字,这并不是很壮观,我当然知道,但是我并没有编写代码来防止类型溢出那不在问题的范围之内。
–安德烈
17年5月5日在11:30
评论
如果您的libc实现不支持z修饰符,则将其强制转换为unsigned long是最佳选择。 C99标准建议size_t的整数转换等级不要大于long,因此您相当安全是否可能与c中平台无关的size_t格式说明符重复?
使用printf打印size_t的正确方法是什么?
在Windows平台上,size_t可以大于long。出于兼容性原因,long始终为32位,但size_t可以为64位。因此,强制转换为无符号长整数可能会丢失一半的位。抱歉:-)
可能的重复方式是使用printf打印size_t的正确方法是什么?