# 格式化输出 ## 无符号数的输出 无符号数可以以八进制、十进制和十六进制的形式输出,它们对应的格式控制符分别为: | | unsigned short | unsigned int | unsigned long | | -------- | -------------- | ------------ | ------------- | | 八进制 | %ho | %o | %lo | | 十进制 | %hu | %u | %lu | | 十六进制 | %hx 或者 %hX | %x 或者 %X | %lx 或者 %lX | 上节我们也讲到了不同进制形式的输出,但是上节我们还没有讲到正负数,所以也没有关心这一点,只是“笼统”地介绍了一遍。现在本节已经讲到了正负数,那我们就再深入地说一下。 严格来说,格式控制符和整数的符号是紧密相关的,具体就是: - %d 以十进制形式输出有符号数; - %u 以十进制形式输出无符号数; - %o 以八进制形式输出无符号数; - %x 以十六进制形式输出无符号数。 那么,如何以八进制和十六进制形式输出有符号数呢?很遗憾,printf 并不支持,也没有对应的格式控制符。在实际开发中,也基本没有“输出负的八进制数或者十六进制数”这样的需求,我想可能正是因为这一点,printf 才没有提供对应的格式控制符。 下表全面地总结了不同类型的整数,以不同进制的形式输出时对应的格式控制符(`--`表示没有对应的格式控制符)。 | | short | int | long | unsigned short | unsigned int | unsigned long | | -------- | ----- | ---- | ---- | -------------- | ------------ | ------------- | | 八进制 | -- | -- | -- | %ho | %o | %lo | | 十进制 | %hd | %d | %ld | %hu | %u | %lu | | 十六进制 | -- | -- | -- | %hx 或者 %hX | %x 或者 %X | %lx 或者 %lX | 有读者可能会问,上节我们也使用 %o 和 %x 来输出有符号数了,为什么没有发生错误呢?这是因为: - 当以有符号数的形式输出时,printf 会读取数字所占用的内存,并把最高位作为符号位,把剩下的内存作为数值位; - 当以无符号数的形式输出时,printf 也会读取数字所占用的内存,并把所有的内存都作为数值位对待。 对于一个有符号的正数,它的符号位是 0,当按照无符号数的形式读取时,符号位就变成了数值位,但是该位恰好是 0 而不是 1,所以对数值不会产生影响,这就好比在一个数字前面加 0,有多少个 0 都不会影响数字的值。 ## 小数的输出 小数也可以使用 printf 函数输出,包括十进制形式和指数形式,它们对应的格式控制符分别是: - %f 以十进制形式输出 float 类型; - %lf 以十进制形式输出 double 类型; - %e 以指数形式输出 float 类型,输出结果中的 e 小写; - %E 以指数形式输出 float 类型,输出结果中的 E 大写; - %le 以指数形式输出 double 类型,输出结果中的 e 小写; - %lE 以指数形式输出 double 类型,输出结果中的 E 大写。 下面的代码演示了小数的表示以及输出: ```c #include #include int main() { float a = 0.302; float b = 128.101; double c = 123; float d = 112.64E3; double e = 0.7623e-2; float f = 1.23002398; printf("a=%e \nb=%f \nc=%lf \nd=%lE \ne=%lf \nf=%f\n", a, b, c, d, e, f); return 0; } ``` 对代码的说明: \1) %f 和 %lf 默认保留六位小数,不足六位以 0 补齐,超过六位按四舍五入截断。 \2) 将整数赋值给 float 变量时会变成小数。 \3) 以指数形式输出小数时,输出结果为科学计数法;也就是说,尾数部分的取值为:0 ≤ 尾数 < 10。 \4) b 的输出结果让人费解,才三位小数,为什么不能精确输出,而是输出一个近似值呢?这和小数在内存中的存储形式有关,很多简单的小数压根不能精确存储,所以也就不能精确输出。 另外,小数还有一种更加智能的输出方式,就是使用`%g`。%g 会对比小数的十进制形式和指数形式,以最短的方式来输出小数,让输出结果更加简练。所谓最短,就是输出结果占用最少的字符。 ## 数字的后缀 一个数字,是有默认类型的:对于整数,默认是 int 类型;对于小数,默认是 double 类型。 请看下面的例子: ``` long a = 100; int b = 294; float x = 52.55; double y = 18.6; ``` 100 和 294 这两个数字默认都是 int 类型的,将 100 赋值给 a,必须先从 int 类型转换为 long 类型,而将 294 赋值给 b 就不用转换了。 52.55 和 18.6 这两个数字默认都是 double 类型的,将 52.55 赋值给 x,必须先从 double 类型转换为 float 类型,而将 18.6 赋值给 y 就不用转换了。 如果不想让数字使用默认的类型,那么可以给数字加上后缀,手动指明类型: - 在整数后面紧跟 l 或者 L(不区分大小写)表明该数字是 long 类型; - 在小数后面紧跟 f 或者 F(不区分大小写)表明该数字是 float 类型。 请看下面的代码: ``` long a = 100l; int b = 294; short c = 32L; float x = 52.55f; double y = 18.6F; float z = 0.02; ``` 加上后缀,虽然数字的类型变了,但这并不意味着该数字只能赋值给指定的类型,它仍然能够赋值给其他的类型,只要进行了一下类型转换就可以了。 对于初学者,很少会用到数字的后缀,加不加往往没有什么区别,也不影响实际编程,但是既然学了C语言,还是要知道这个知识点的,万一看到别人的代码这么用了,而你却不明白怎么回事,那就尴尬了。