printf
函数
printf
函数被设计用来显示
格式串
(format string)的内容,并且在该串中的指定位置插入可能的值。调用
printf
函数时必须提供格式串,格式串后面的参数是需要在显示时插入到该串中的值:
printf(格式串, 表达式1, 表达式2, ...);
显示的值可以是常量、变量或者更加复杂的表达式。
格式串包含普通字符和
转换说明
(conversion specification),其中转换说明以字符
%
开头。转换说明是用来表示打印过程中待填充的值的占位符。跟随在字符
%
后边的信息指定了把数值从内部形式(二进制)
转换
成打印形式(字符)的方法,这就是“转换说明”这一术语的由来。例如,转换说明
%d
指定
printf
函数把
int
型值从二进制形式转换成十进制数字组成的字符串,转换说明
%f
对
float
型值也进行类似的转换。
格式串中的普通字符完全按照它们在字符串中出现的那样显示出来,而转换说明则要用待显示的值来替换。思考下面的例子:
int i, j;
float x, y;
i = 10;
j = 20;
x = 43.2892f;
y = 5527.0f;
printf("i = %d, j = %d, x = %f, y = %f\n", i, j, x, y);
这个
printf
函数调用会产生如下输出:
i = 10, j = 20, x = 43.289200, y = 5527.000000
格式串中的普通字符被简单复制给输出行,而变量
i
、
j
、
x
和
y
的值则依次替换了4个转换说明。
C语言编译器不会检测格式串中转换说明的数量是否和输出项的数量相匹配。下面这个
printf
函数调用所拥有的转换说明的数量就多于要显示的值的数量:
printf("%d %d\n", i); /*** WRONG ***/
printf
函数将正确显示变量
i
的值,接着显示另一个(无意义的)整数值。函数调用带有太少的转换说明也会出现类似的问题:
printf("%d\n", i, j); /*** WRONG ***/
在这种情况下,
printf
函数会显示变量
i
的值,但是不显示变量
j
的值。
此外,C语言编译器也不检测转换说明是否适合要显示项的数据类型。如果程序员使用不正确的转换说明,程序将简单地产生无意义的输出。思考下面的
printf
函数调用,其中
int
型变量
i
和
float
型变量
x
的顺序放置错误:
printf("%f %d\n", i, x); /*** WRONG ***/
因为
printf
函数必须服从于格式串,所以它将如实地显示出一个
float
型值,接着是一个
int
型值。可惜这两个值都是无意义的。
转换说明给程序员提供了大量对输出格式的控制方法。另外,转换说明可能很复杂且难以阅读。事实上,在本节中想要完整详尽地介绍转换说明是不可能的,这里只是简要地介绍一些较为重要的性能。
在第2章中我们已经看到,转换说明可以包含格式化信息。具体来说,我们可以用
%.1f
来显示小数点后带一位数字的
float
型值。更一般地,转换说明可以用
%
m.pX
格式或
%
-
m.pX
格式,这里的
m
和
p
都是整型常量,而
X
是字母。
m
和
p
都是可选的。如果省略
p
,
m
和
p
之间的小数点也要去掉。在转换说明
%10.2f
中,
m
是
10
,
p
是
2
,而
X
是
f
。在转换说明
%10f
中,
m
是
10
,
p
(连同小数点一起)省去了;而在转换说明
%.2f
中,
p
是
2
,
m
省去了。
最小栏宽
(minimum field width)
m
指定了要显示的最少字符数量。如果要显示的数值所需的字符数少于
m
,那么值在字段内是右对齐的。(换句话说,在值前面放置额外的空格。)例如,转换说明
%4d
将以
·123
的形式显示数123(本章用符号
·
表示空格字符)。如果要显示的值所需的字符数多于
m
,那么栏宽会自动扩展为所需的尺寸。因此,转换说明
%4d
将以
12345
的形式显示数12345,而不会丢失数字。在
m
前放上一个负号会导致左对齐;转换说明
%-4d
将以
123·
的形式显示123。
精度 (precision) p 的含义很难描述,因为它依赖于 转换指定符 (conversion specifier) X 的选择。 X 表明在显示数值前需要对其进行哪种转换。对数值来说最常用的转换指定符有以下几个。
d
——表示十进制(基数为10)形式的整数。
p
指明了待显示数字的最少个数(必要时在数前加上额外的零);如果省略
p
,则默认它的值为1。
e
——表示指数(科学记数法)形式的浮点数。
p
指明了小数点后应该出现的数字个数(默认值为6)。如果
p
为0,则不显示小数点。
f
——表示“定点十进制”形式的浮点数,没有指数。
p
的含义与说明符
e
中的一样。
g
——表示指数形式或者定点十进制形式的浮点数,形式的选择根据数的大小决定。
p
意味着可以显示的有效数字(
不是
小数点后的数字)的最大数量。与转换指定符
f
不同,
g
的转换将不显示尾随的零。此外,如果要显示的数值没有小数点后的数字,
g
就不会显示小数点。
编写程序时无法预知数的大小或者数值变化范围很大的情况下,说明符
g
对于数的显示是特别有用的。在用于显示大小适中的数时,说明符
g
采用定点十进制形式。但是,在显示非常大或非常小的数时,说明符
g
会转换成指数形式以便减少所需的字符数。
除了
%d
、
%e
、
%f
和
%g
以外,还有许多其他的说明符[整型说明符(➤7.1节)、浮点型说明符(➤7.2节)、字符说明符(➤7.3节)和字符串说明符(➤13.3节)]。我们将在后续章节中陆续进行介绍。转换指定符的全部列表以及转换指定符其他性能的完整解释见22.3节。
程序 用
printf
函数格式化数
下面的程序举例说明了用
printf
函数以各种格式显示整数和浮点数的方法。
tprintf.c
/* Prints int and float values in various formats */
#include <stdio.h>
int main(void)
{
int i;
float x;
i = 40;
x = 839.21f;
printf("|%d|%5d|%-5d|%5.3d|\n", i, i, i, i);
printf("|%10.3f|%10.3e|%-10g|\n", x, x, x);
return 0;
}
在显示时,
printf
函数格式串中的字符
|
只是用来帮助显示每个数所占用的空格数量。不同于
%
或
\
,字符
|
对
printf
函数而言没有任何特殊意义。此程序的输出如下:
︱40︱ 40︱40 ︱ 040︱
︱ 839.210︱ 8.392e+02︱839.21 ︱
下面仔细看一下上述程序中使用的转换说明。
%d
——以十进制形式显示变量
i
,且占用最少的空间。
%5d
——以十进制形式显示变量
i
,且至少占用5个字符的空间。因为变量
i
只占2个字符,所以添加了3个空格。
%-5d
——以十进制形式显示变量
i
,且至少占用5个字符的空间。因为表示变量
i
的值不需要用满5个字符,所以在后续位置上添加空格(更确切地说,变量
i
在长度为5的字段内是左对齐的)。
%5.3d
——以十进制形式显示变量
i
,至少占用5个字符的空间,并至少有3位数字。因为变量
i
只有2个字符长度,所以要添加一个额外的零来保证有3位数字。现在只有3个字符长度,为了保证占有5个字符,还要添加2个空格(变量
i
是右对齐的)。
%10.3f
——以定点十进制形式显示变量
x
,且总共用10个字符,其中小数点后保留3位数字。因为变量
x
只需要7个字符(即小数点前3位,小数点后3位,再加上小数点本身1位),所以在变量
x
前面有3个空格。
%10.3e
——以指数形式显示变量
x
,且总共用10个字符,其中小数点后保留3位数字。因为变量
x
总共需要9个字符(包括指数),所以在变量
x
前面有1个空格。
%-10g
——既可以以定点十进制形式显示变量
x
,也可以以指数形式显示变量
x
,且总共用10个字符。在这种情况下,
printf
函数选择用定点十进制形式显示变量
x
。负号会进行强制左对齐,因此有4个空格跟在变量
x
后面。
格式串中常用的代码
\n
被称为
转义序列
(escape sequence)。转义序列(➤7.3节)使字符串包含一些特殊字符而不会使编译器引发问题,这些字符包括非打印的(控制)字符和对编译器有特殊含义的字符(如
"
)。后面会提供完整的转义序列表,现在先看一组示例。
\a
。
\b
。
\n
。
\t
。
当这些转义序列出现在
printf
函数的格式串中时,它们表示在显示中执行的操作。在大多数机器上,输出
\a
会产生一声鸣响,输出
\b
会使光标从当前位置回退一个位置,输出
\n
会使光标跳到下一行的起始位置,
输出
\t
会把光标移动到下一个制表符的位置。
字符串可以包含任意数量的转义序列。思考下面的
printf
函数示例,其中的格式串包含了6个转义序列:
printf("Item\tUnit\tPurchase\n\tPrice\tDate\n");
执行上述语句显示出一条两行的标题:
Item Unit Purchase
Price Date
另一个常用的转义序列是
\"
,它表示字符
"
。因为字符
"
标记字符串的开始和结束,所以它不能出现在没有使用上述转义序列的字符串内。下面是一个示例:
printf("\"Hello!\"");
这条语句产生如下输出:
"Hello!"
附带提一下,不能在字符串中只放置单独一个字符
\
,编译器将认为它是一个转义序列的开始。为了显示单独一个字符
\
,需要在字符串中放置两个字符
\
:
printf("\\"); /* prints one \ character */