预计阅读本页时间:-
浮点型参数的转换
有用于打印浮点类型double和long double的转换说明符,但没有用于float的说明符。原因是在K&R C中float值在被用于表达式中或者被用作参数之前,会被自动转换为double类型。一般情况下,ANSI C不会自动把float转换成double。不过,为了保护大量现有的假设float参数会被转换成double的程序,printf()和其他任何不使用显式原型的C函数的所有float参数仍然会自动被转换成double。因此,不管是K&R C还是ANSIC,都无需专门的转换说明符来显示float类型。
表4.5 printf()的标志
标 志 | 意 义 |
---|---|
- | 项目是左对齐的;也就是说,会把项目打印在字段的左侧开始处 示例:“%-20s” |
+ | 有符号的值若为正,则显示带加号的符号;若为负,则带减号的符号 示例:“%+6.2f” |
(空格) | 有符号的值若为正,则显示时带前导空格(但是不显示符号);若为负,则带减号符号。+标志会 覆盖空格标志 示例:“%6.2f” |
# | 使用转换说明的可选形式。若为%o格式,则以0开始;若为%x和%X格式,则以0x或0X开始。 对于所有的浮点形式,#保证了即使不跟任何数字,也打印一个小数点字符。对于%g和%G格式, 它防止尾随零被删除 示例:“%#o”、“%#8.0f”和“%+#10.3E” |
0 | 对于所有的数字格式,用前导零而不是用空格填充字段宽度。如果出现-标志或者指定了精度(对 于整数)则忽略该标志 示例:“%010d”和“%08.3f” |
一、使用修饰符和标志示例
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
我们开始使用这些修饰符,先来看看打印整数时字段宽度修饰符的作用。考虑程序清单4.7中的程序。
程序清单4.7 width.c程序
程序清单4.7使用4个不同的转换说明把相同的值打印了4次。它使用一个星号(*)来标识出每个字段的开始和结尾。输出结果如下:
*931*
*931*
* 931*
*931 *
第一个转换说明是不带任何修饰符的%d。它生成了一个与要打印的整数宽度相同的字段。这是默认选项:也就是说,如果您没有进一步给出命令,这就是打印的结果。第二个转换说明是%2d。它指示应该产生宽度为2的字段,但是由于该整数有3位数字,所以字段自动扩展以适应数字的长度。下一个转换说明是%10d。这将产生一个有10个空格那么宽的字段,于是在星号之间有7个空白和3个数字,并且数字位于整个字段的右端。最后一个说明是%-10d。它也产生宽10个空格的字段,标志“-”把数字放到左端,就像广告中那样。您习惯以后,该系统就易于使用,并使您能够很好地控制输出结果的外观。试着改变PAGES的值,看看不同位数的数字是如何打印的。
现在来看一些浮点格式。请输入、编译并运行程序清单4.8中的程序。
程序清单4.8 floats.c程序
这一次,程序使用关键字const创建了一个符号常量。输出为:
*3852.990000*
*3.852990e+03*
*3852.99*
*3853.0*
* 3852.990*
* 3.863e+03*
*+3852.99*
*0003852.99*
本例以默认格式%f开始。在这种情形下,有两个默认项目:字段宽度和小数点右边的数字的数目。第二个默认项目的值是6个数字,字段宽度就是容纳数字所用的空间。
接下来是%e的默认格式。它在小数点的左侧打印一个数字,在小数点的右侧打印6个数字。我们得到了一堆数字!解决方法是指定小数点右边小数位的数目,本段中接下来的4个示例就是这样做的。请注意,第4个和第6个示例对输出进行了四舍五入。
最后,+标志使得结果数字和它的代数符号一起打印,在这里该符号就是加号符号;0标志产生前导零以使结果填充整个字段。请注意,在说明符%010中第一个0是一个标志,剩余的数字(10)指定字段宽度。
您可以修改RENT值来看看不同大小的值如何打印。程序清单4.9给出了另外一些组合。
程序清单4.9 flags.c程序
输出如下:
1f 1F 0x1f
**42** 42**-42**
** 6** 006**00006** 006**
首先,If等于31的十六进制数。x说明符输出1f,而X说明符输出1F。使用#标志使输出以0x开始。
第二行示范了如何在说明符中使用空格以在正值之前产生一个前导空格(在负值之前不产生前导空格)。这将使有效位相同的正值和负值以相同字段宽度打印输出,因此结果看起来会令人舒服一些。
第三行说明如何在整数格式中使用精度说明符(%5.3d)来产生足够的前导零以填满要求的最小数字位数(这里是3);而使用0标志将会用前导零填满整个字段宽度;最后,如果0标志和精度说明符同时出现,那么0标志就会被忽略。
现在我们查看一些有关字符串的选项。考虑程序清单4.10中的示例:
程序清单4.10 strings.c程序
下面是输出:
/Authentic imitation! /
/ Authentic imitation! /
/ Authe/
/Authe /
请注意系统如何扩展字段以包含所有指定的字符。同时请注意:精度说明符是如何限制所打印的字符的数目的。格式说明符中的.5告诉printf()只打印5个字符。另外,“-”修饰符使文本左对齐输出。
二、应用您的知识
现在,您已经看到了几个示例。如何用一个语句打印如下形式的内容?
The NAME family just may be $XXX.XX dollars richer!
这里,NAME和XXX.XX代表由程序中的变量(比如说name[40]和cash)所提供的值。
解决方法如下:
printf (“The %s family just may be $%.2f richer! \n”, name, cash);
4.4.4 转换说明的意义
我们深入探讨一下转换说明的意义。它把存储在计算机中的二进制格式的数值转换成一系列字符(一个字符串)以便于显示。例如,数字76的内部存储形式可能是二进制的01001100。%d转换说明符将之转换成字符7和6,并显示成76。%x转换则把相同的值(01001100)转换成十六进制的表示法4c。%c把相同的值转换成字符表示法L。
术语“转换”(conversion)可能会带来误导,因为它可能意味着用转换后的值代替原值。转换说明实际上就是翻译说明;%d意为“把给定的值翻译成十进制整数文本表示,并打印出来”。
一、不匹配的转换
显然,应该使转换说明与要打印的值的类型相匹配。通常情况下都有多种选择。例如,如果想打印一个int类型值,可以使用%d、&x或%o,所有这些说明符都假定打印一个int类型的值;它们仅仅提供该值不同的表示形式。同样,也可以使用%f、%e或%g来表示double类型的值。
如果转换说明与类型不匹配会怎样呢?上章中您已看到不匹配会导致一些问题。这一点是非常重要的,一定要牢记。所以程序清单4.11又给出了整数系列内几个不匹配的示例。
程序清单4.11 intconv.c程序
我们的系统产生如下结果:
num as short and unsigned short: 336 336
-num as short and unsigned short: -336 65200
num as int and char: 336 P
WORDS as int, short, and char: 65618 82 R
请看第一行,您会看到%hd和%hu产生336作为变量num的输出,这没有任何问题。然而,mnum的%u(无符号)版本的输出结果则为65200,而非您所期望的336。这是由于有符号short int值在我们的参考系统中的表示方式所造成的。首先,它们的大小为2字节。其次,该系统使用一种被称为2的补码(two's complement)的方法来表示有符号整数。在这种方法中,数字0到32767代表它们本身,而数字32768到65535则代表负数,65535代表-1、65534代表-2,以此类推。因此,-336由65536-336,也即65200来表示。所以当被解释成一个有符号整数时,65200代表-336;而被解释成无符号整数时,65200则代表65200。一定要谨慎! 一个数字可以被解释成两个不同的值。不是所有的系统都用该方法来表示负整数。虽然如此,还是有一个准则:不要期望%u转换能把数字和符号分开。
第二行显示如果您试图把一个大于255的值转换成字符,将会发生什么事情。在该系统上,一个short int占用2个字节,一个char占用1个字节。当printf()使用%c打印336时,它只查看用于存放336的两个字节中的1个字节。这种截断(请参见图4.8)相当于用256除一个整数,并取其余数。在这种情况下,余数是80,也就是字符P的ASCII码值。更技术一些,您可以说,该数字被解释成“以256为模”(modulo 256),意即使用数字被256除的余数。
图4.8 把336读作一个字符
最后,我们试着打印一个比系统允许的最大的short int (32767)大的整数(65618)。这次计算机又进行了模运算。我们的系统根据数字65618的大小,将它存储为4个字节的整数值。当我们使用%hd说明打印它时,printf()只使用最后2个字节。这相当于使用被65536除后得到的余数。在这里,余数是82。鉴于负数的存储方法,在32767和65536之间的余数会被打印成负数。整数大小不同的系统将会做出相同的动作,但是会产生不同的数值。
当混淆了整数和浮点类型时,结果更是千奇百怪。例如,考虑程序清单4.12。
程序清单4.12 floatcnv.c程序
在我们的系统上,程序清单4.12产生如下输出:
输出的第一行显示,使用%e说明符并没有把整数转换成浮点数。例如,考虑当您试图使用%e说明符打印n3(long类型)时,发生了什么。首先,%e说明符使printf( )期望一个double类型值,在本系统这是一个8字节的值。当printf( )查看n3(它在本系统中是一个4字节值)时,它也查看邻近的4个字节。因此,它查看了一个8字节的单元,其中只有一部分是真正的n3。接着,它把该单元内的位解释成一个浮点数。例如,一些位将会被解释成指数。所以即使n3的位数正确,在%e作用下和%ld作用下它们的解释也是不同的。最终结果是无意义的。
第一行也说明了前面所提到的:当被用作printf( )的参数时,float被转换成double。在本系统中,float是4字节的,但是n1会被扩展到8字节以使printf( )能够正确显示它。
输出的第二行显示,如果使用的说明符正确,printf( )就可以正确打印n3和n4。
输出的第三行表明,如果printf( )语句在其他地方出现不匹配错误,即使正确的说明符也会产生虚假的结果。正如您能料到的,试图用%ld说明符打印一个浮点数会失败。但是在这里,试图使用%ld打印一个long类型居然也失败了!问题出在C把信息传递给函数的方式中。失败过程的确切细节是依赖于实现的,下面这段文字针对一个具有代表性的系统进行了讨论。