在看代码的时候,看到个怪怪的符号,printf("%zd\n", s),见过奇怪的,没见过这么奇怪的format格式(原谅我的孤陋寡闻吧^-^),想破脑袋也没想起来在哪本书上见过这个家伙。得,乖乖的google一下吧。
网上的资料不是特别多,而且翻来复去都是同一套说法,全是大家粘贴复制的成果。不过有人曾经在stack overflow中提过一个问题:How to use “zd” specifier with `printf()`?
还真有牛人做了详尽的回复,先弄过来慢慢咀嚼。链接:
以下是大牛的回复:
printf
with a "%zd"
format expects an argument of the signed type that corresponds to the unsigned type size_t
.
Standard C doesn't provide a name for this type or a good way to determine what it is. If size_t
is a typedef for unsigned long
, for example, then "%zd"
expects an argument of type long
, but that's not a portable assumption.
The standard requires that corresponding signed and unsigned types use the same representation for the non-negative values that are representable in both types. A footnote says that this is meant to imply that they're interchangeable as function arguments. So this:
size_t s = 42;
printf("s = %zd\n", s);
should work, and should print "42". It will interpret the value 42
, of the unsigned type size_t
, as if it were of the corresponding signed type. But there's really no good reason to do that, since "%zu"
is also correct and well defined, without resorting to additional language rules. And "%zu"
works for allvalues of type size_t
, including those outside the range of the corresponding signed type.
Finally, POSIX defines a type ssize_t
in the headers <unistd.h>
and <sys/types.h>
. Though POSIX doesn't explicitly say so, presumably ssize_t
will be the signed type corresponding to size_t
. So if you're writing POSIX-specific code, "%zd"
is (probably) the correct format for printing values of type ssize_t
.
意思是什么呢?
维基百科中对printf格式有详细的介绍,感兴趣的可以移步https://en.wikipedia.org/wiki/Printf_format_string。其中printf的语法格式为:
%[parameter][flags][width][.precision][length]type
%zd对照过来,z属于length字段,d属于type字段。该表达式用来表明输出格式是长度型(size_t)的有符号整形值。其中size_t在不同位数的操作系统中定义也是不相同的:
32位
typedef unsigned int size_t
typedef int ssize_t
64位
typedef long unsigned int size_t
typedef long int ssize_t
因此在程序设计的时候,如果参数类型是size_t或者ssize_t,在进行格式化输入或输出的时候务必使用‘z’修饰符,以防止显示异常。常见的%d, %x等表示的是整形值,最多表示4个字节,而在64位系统中,数据有可能需要用更多的字节表示,所以用size_t定义,就可以跨系统使用,不用担心溢出问题。对应的格式化显示的时候,也就不存在问题。明白了这个点,就可以理解‘z’本质是和'l'等效。‘z’只控制长度,具体是有符号还是无符号值,还是由后面的类型来决定。我之前的理解是有误的。
使用64位linux系统进行了简单验证:
#include <stdio.h>
int main (void)
{ int i;
size_t uzs = 1;
ssize_t zs = -1;
size_t test = 0xffffffffffffffff;
ssize_t stest = 0x8000000000000000;
for ( i= 0; i<5 ;i++, uzs <<= 16,zs <<= 16 )
{
printf ("uzs %%zu:%zu\n", uzs);
printf ("uzs %%zd:%zd\n", uzs);
printf ("uzs %%x:0x%lx\n", uzs);
printf ("zs %%ld:%ld\n", zs);
printf ("zs %%zu:%zu\n", zs);
printf ("zs %%zd:%zd\n", zs);
printf ("zs %%x:0x%lx\n", zs);
printf("\n");
}
printf("test is %d\n", test);
printf("test is %ld\n", test);
printf("test is %zd\n", test);
printf("test is %u\n", test);
printf("test is %lu\n", test);
printf("test is %zu\n", test);
printf("stest is %d\n", stest);
printf("stest is %ld\n", stest);
printf("stest is %zd\n", stest);
printf("stest is %u\n", stest);
printf("stest is %lu\n", stest);
printf("stest is %zu\n", stest);
return 0;
}
编译的时候会有如下警告:
test.c: In function ‘main’:
test.c:20:9: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘size_t {aka long unsigned int}’ [-Wformat=]
printf("test is %d\n", test);
^
test.c:23:9: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘size_t {aka long unsigned int}’ [-Wformat=]
printf("test is %u\n", test);
^
test.c:28:9: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘ssize_t {aka long int}’ [-Wformat=]
printf("stest is %d\n", stest);
^
test.c:31:9: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘ssize_t {aka long int}’ [-Wformat=]
printf("stest is %u\n", stest);
^
可以看到size_t和ssize_t的实际类型。
最终的输出结果:
uzs %zu:1
uzs %zd:1
uzs %x:0x1
zs %ld:-1
zs %zu:18446744073709551615
zs %zd:-1
zs %x:0xffffffffffffffff
uzs %zu:65536
uzs %zd:65536
uzs %x:0x10000
zs %ld:-65536
zs %zu:18446744073709486080
zs %zd:-65536
zs %x:0xffffffffffff0000
uzs %zu:4294967296
uzs %zd:4294967296
uzs %x:0x100000000
zs %ld:-4294967296
zs %zu:18446744069414584320
zs %zd:-4294967296
zs %x:0xffffffff00000000
uzs %zu:281474976710656
uzs %zd:281474976710656
uzs %x:0x1000000000000
zs %ld:-281474976710656
zs %zu:18446462598732840960
zs %zd:-281474976710656
zs %x:0xffff000000000000
uzs %zu:0
uzs %zd:0
uzs %x:0x0
zs %ld:0
zs %zu:0
zs %zd:0
zs %x:0x0
test is -1
test is -1
test is -1
test is 4294967295
test is 18446744073709551615
test is 18446744073709551615
stest is 0
stest is -9223372036854775808
stest is -9223372036854775808
stest is 0
stest is 9223372036854775808
stest is 9223372036854775808
从上面的结果来看,%zd 和 %ld结果一致,%zu 和%lu结果一致,和前面的描述分析也是吻合的。另外从这个问题延申来看,对于编译器警告还是要有足够的重视,警告意味着代码的边界条件。
==============================================================
分割线
===============================================================
最后,统一总结下格式说明符
表一 转换说明符及作为结果的打印输出
表二 printf() 修饰符
表三 printf()的标志
表四 ANSIC 中 scanf()的转换说明符
表五 scanf()的转换修饰符
表附一 特别说明符
最后小结一下,一个字节的用%c,%hhu,%hhd等,两个字节用%hu,%hd等,四个字节用%d,%u等8个字节的用%lld,%llu等. 尽量不要用%I64d这样不兼容的格式。
转载自原文链接, 如需删除请联系管理员。
原文链接:printf中的format格式 %zd,转载请注明来源!