首页 » 技术分享 » printf中的format格式 %zd

printf中的format格式 %zd

 

      在看代码的时候,看到个怪怪的符号,printf("%zd\n", s),见过奇怪的,没见过这么奇怪的format格式(原谅我的孤陋寡闻吧^-^),想破脑袋也没想起来在哪本书上见过这个家伙。得,乖乖的google一下吧。

    网上的资料不是特别多,而且翻来复去都是同一套说法,全是大家粘贴复制的成果。不过有人曾经在stack overflow中提过一个问题:How to use “zd” specifier with `printf()`

还真有牛人做了详尽的回复,先弄过来慢慢咀嚼。链接:

http://stackoverflow.com/questions/32916575/how-to-use-zd-specifier-with-printf?answertab=oldest#tab-top

 

以下是大牛的回复:

 

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,转载请注明来源!

0