printf()打印有符号负数会多出0xff
#include <stdio.h> int main() { char char_a = 0x88; short short_a = 0x8848; unsigned char u_char_a = 0x88; unsigned short u_short_a = 0x8848; printf("0_char_a_x= 0x%x\n", char_a); printf("1_short_a_x = 0x%x\n", short_a);
printf("2_u_char_a_x = 0x%x\n", u_char_a); printf("3_u_short_a_x = 0x%x\n", u_short_a); printf("4_char_a_x_(u) = 0x%x\n", (unsigned char)char_a); printf("5_short_a_x_(u) = 0x%x\n", (unsigned short)short_a); printf("6_short_a_hx = 0x%hx\n", short_a); printf("7_char_a_hx = 0x%hx\n", char_a); printf("8_char_a_hx_(u) = 0x%hx\n", (unsigned char)char_a); printf("size of 'A' = %d\n", sizeof 'A'); return 0; }
如上代码的打印结果:
0_char_a_x= 0xffffff88
1_short_a_x = 0xffff8848
2_u_char_a_x = 0x88
3_u_short_a_x = 0x8848
4_char_a_x_(u) = 0x88
5_short_a_x_(u) = 0x8848
6_short_a_hx = 0x8848
7_char_a_hx = 0xff88
8_char_a_hx_(u) = 0x88
size of 'A' = 4
如果以二进制“%x”打印一个有符号char或者short类型的数据,而恰好这个数是负的,最高位为1,那么会在高位多打印多个'f',见第0、1行打印。就算用"%2x"或者"%4x"去限制输出位数,还是会多打印'f'。如果定义时使用无符号类型,则不会有这样的困扰,见第2、3行打印。
解决方法也不难,“%x”对应的参数,强制转换成相应的无符号类型即可,见第4、5行打印。针对short类型,也可以用"%hx",让操作符和操作数相匹配,可以正好输出4个字符以表示4位十六进制,见第6行打印。
会多打印很多'f'的原因,是编译器偷偷进行了类型提升。在涉及类型小于int或double时,可能会出现类型提升。
在本例中,%x期待的参数类型其实是int,但是偏偏塞给它char和short,编译器就会偷偷将char和short进行整型提升到int类型。发生int整型提升时,如果int可以完整地容纳原先的数据,那么不管是有无符号的char或是short,以及枚举类型,将被提升为int;如果int容纳不了,则被提升为unsigned int。printf中使用“%hx”打印char,同样也会多出2个ff,道理类似,不过是将char类型提升到了short类型,用无符号类型的强制转换也可以使其输出2位而非4位十六进制数,见第7、8行打印。
为什么用"%nx"去限制输出n位,还是会多打印'f'?因为当原位数大于n时,仍按原位数打印。负的数值已经做了整型类型提升,0x80提升后的0xffffff80,确实是要按8位16进制打印,“%4x”是限制不住的。或者说用“%4x”“%4f”又或是“%4s”是不能限制输出的位数的,只能是在原先位数不足的情况下,将前部空出的部分补齐。
为什么无符号类型,或是加了(unsigned xxx)强制转换的情况,“似乎可以避免”被类型提升呢。其实无符号数也被整型提升过了,与有符号数不同的是,无符号数的整型提升是在高位补0,而有符号数的整型提升是在高位补符号位,对于负值来说,是在高位加了n个1。在以“%x”十六进制数解释操作数时,高位的多个0不会显式地打印出来,而高位的n个1,则以多个“f”的形式打印出来,也就出现了见第0、1行打印的现象。可以尝试在赋初值时给char_a一个正值,整型提升后补上正数的符号位0,看上去也与无符号类型的整型提升打印一样。
在本例最后打印一个字符常量的长度,它不是char的长度1,而是int的长度4,也是发生了整型提升的原因。