printf什么意思c语言(格式化输出函数——printf功能详解)
第 3 章 格式化输入/输出
在探索难以实现的问题时,问题本身的简单性只会使情况更糟。
scanf函数和printf函数是C语言编程中使用得很频繁的两个函数,它们用来格式化输入和输出。正如本章要展示的那样,虽然这两个函数功能强大,用好它们却不容易。3.1节描述printf函数,3.2节介绍scanf函数。但是这两节的介绍都不完整,完整的细节将留到第22章中介绍。
3.1 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型值。可惜这两个值都是无意义的。
3.1.1 转换说明
转换说明给程序员提供了大量对输出格式的控制方法。另外,转换说明可能很复杂且难以阅读。事实上,在本节中想要完整详尽地介绍转换说明是不可能的,这里只是简要地介绍一些较为重要的性能。
在第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后面。
3.1.2 转义序列
格式串中常用的代码\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 */
3.2 scanf函数
就如同printf函数用特定的格式显示输出一样,scanf函数也根据特定的格式读取输入。像printf函数的格式串一样,scanf函数的格式串也可以包含普通字符和转换说明两部分。scanf函数转换说明的用法和printf函数转换说明的用法本质上是一样的。
在许多情况下,scanf函数的格式串只包含转换说明,如下例所示:
int i, j; float x, y; scanf("%d%d%f%f", &i, &j, &x, &y);
假设用户录入了下列输入行:
1 -20 .3 -4.0e3
scanf函数将读入上述行的信息,并且把这些符号转换成它们表示的数,然后分别把1、-20、0.3和-4000.0赋值给变量i、j、x和y。scanf函数调用中像"%d%d%f%f"这样“紧密压缩”的格式串是很普遍的,而printf函数的格式串很少有这样紧挨着的转换说明。
像prinf函数一样,scanf函数也有一些不易觉察的陷阱。使用scanf函数时,程序员必须检查转换说明的数量是否与输入变量的数量相匹配,并且检查每个转换是否适合相对应的变量。与printf函数一样,编译器无法检查出可能的匹配不当。另一个陷阱与符号&有关,符号&通常被放在scanf函数调用中每个变量的前面。符号&常常(但不总是)是需要的,记住使用它是程序员的责任。
如果scanf函数调用中忘记在变量前面放置符号&,将产生不可预知甚至可能是毁灭性的结果。程序崩溃是常见的结果。最轻微的后果则是从输入读进来的值无法存储到变量中,变量将保留原有的值(如果没有给变量赋初始值,那么这个原有值可能是没有意义的)。忽略符号&是极为常见的错误,一定要小心!一些编译器可以检查出这种错误,并产生一条类似“format argument is not a pointer”的警告消息。(术语指针将在第11章定义,符号&用于创建一个指向变量的指针。)如果抛出警告消息,检查一下是否遗漏了符号&。
调用scanf函数是读数据的一种有效但不理想的方法。许多专业的C程序员会避免使用scanf函数,而是采用字符格式读取所有数据,然后再把它们转换成数值形式。在本书中,特别是前面的几章将相当多地用到scanf函数,因为它提供了一种读入数的简单方法。但是要注意,如果用户录入了非预期的输入,那么许多程序都无法正常执行。正如稍后将看到的那样,可以用程序测试scanf函数(➤22.3节)是否成功读入了要求的数据(若不成功,还可以试图恢复)。但是,这样做对于本书的示例是不切实际的,因为这类测试将添加太多语句,从而掩盖示例的要点。
3.2.1 scanf函数的工作方法
实际上scanf函数可以做的事情远远多于目前为止已经提到的这些。scanf函数本质上是一种“模式匹配”函数,试图把输入的字符组与转换说明相匹配。
像printf函数一样,scanf函数是由格式串控制的。调用时,scanf函数从左边开始处理字符串中的信息。对于格式串中的每一个转换说明,scanf函数从输入的数据中定位适当类型的项,并在必要时跳过空格。然后,scanf函数读入数据项,并且在遇到不可能属于此项的字符时停止。如果读入数据项成功,那么scanf函数会继续处理格式串的剩余部分;如果某一项不能成功读入,那么scanf函数将不再查看格式串的剩余部分(或者余下的输入数据),并立即返回。
在寻找数的起始位置时,scanf函数会忽略空白字符(white-space character,包括空格符、水平和垂直制表符、换页符和换行符)。因此,我们可以把数字放在同一行或者分为几行来输入。考虑下面的scanf函数调用:
scanf("%d%d%f%f", &i, &j, &x, &y);
假设用户录入3行输入:
1 -20 .3 -4.0e3
scanf函数会把它们看作一个连续的字符流:
··1¤-20···.3¤···-4.0e3¤
(这里使用符号·表示空格符,符号¤表示换行符。)因为scanf函数在寻找每个数的起始位置时会跳过空白字符,所以它可以成功读取这些数。在接下来的示意代码中,字符下方的s表示此项被跳过,而字符下面的r表示此项被读取为输入项的一部分:
··1¤-20···.3¤···-4.0e3¤ ssrsrrrsssrrssssrrrrrr
scanf函数“忽略”了最后的换行符,实际上没有读取它。这个换行符将是下一次scanf函数调用的第一个字符。
scanf函数遵循什么规则来识别整数或浮点数呢?当要求读入整数时,scanf函数首先寻找正号或负号,然后读取数字,直到读到一个非数字时才停止。当要求读入浮点数时,scanf函数会寻找一个正号或负号(可选),随后是一串数字(可能含有小数点),再往后是一个指数(可选)。指数由字母e(或者字母E)、可选的符号,以及一个或多个数字构成。在用于scanf函数时,转换说明%e、%f和%g是可以互换的,这3种转换说明在识别浮点数方面都遵循相同的规则。
当scanf函数遇到一个不可能属于当前项的字符时, 它会把此字符“放回原处”,以便在扫描下一个输入项或者下一次调用scanf函数时再次读入。思考下面(公认有问题的)4个数的排列:
1-20.3-4.0e3¤
我们使用与以前一样的scanf函数调用:
scanf("%d%d%f%f", &i, &j, &x, &y);
下面列出了scanf函数处理这组新输入的方法。
- 转换说明%d。第一个非空的输入字符是1;因为整数可以以1开始,所以scanf函数接着读取下一个字符,即-。scanf函数识别出字符-不能出现在整数内,因此把1存入变量i中,而把字符-放回原处。
- 转换说明%d。随后,scanf函数读取字符-、2、0和.(句点)。因为整数不能包含小数点,所以scanf函数把–20存入变量j中,而把字符.放回原处。
- 转换说明%f。接下来scanf函数读取字符.、3和-。因为浮点数不能在数字后边有负号,所以scanf函数把0.3存入变量x中,而把字符-放回原处。
- 转换说明%f。最后,scanf函数读取字符-、4、.、0、e、3和¤(换行符)。因为浮点数不能包含换行符,所以scanf函数把存入变量y中,而把换行符放回原处。
在这个例子中,scanf函数能够把格式串中的每个转换说明与一个输入项进行匹配。因为换行符没有读取,所以它将留给下一次scanf函数调用。
3.2.2 格式串中的普通字符
通过编写含有普通字符和转换说明的格式串能进一步地理解模式匹配的概念。处理格式串中的普通字符时,scanf函数采取的动作依赖于这个字符是否为空白字符。
- 空白字符。当在格式串中遇到一个或多个连续的空白字符时,scanf函数从输入中重复读空白字符,直到遇到一个非空白字符(把该字符“放回原处”)为止。格式串中空白字符的数量无关紧要,格式串中的一个空白字符可以与输入中任意数量的空白字符相匹配。(附带提一下,在格式串中包含空白字符并不意味着输入中必须包含空白字符。格式串中的一个空白字符可以与输入中任意数量的空白字符相匹配,包括零个。)
- 其他字符。当在格式串中遇到非空白字符时,scanf函数将把它与下一个输入字符进行比较。如果两个字符相匹配,那么scanf函数会放弃输入字符,并继续处理格式串。如果两个字符不匹配,那么scanf函数会把不匹配的字符放回输入中,然后异常退出,而不进一步处理格式串或者从输入中读取字符。
例如,假设格式串是"%d/%d"。如果输入是
·5/·96
在寻找整数时,scanf函数会跳过第一个空格,把%d与5相匹配,把/与/相匹配,在寻找下一个整数时跳过一个空格,并且把%d与96相匹配。另一方面,如果输入是
·5·/·96
scanf函数会跳过一个空格,把%d与5相匹配,然后试图把格式串中的/与输入中的空格相匹配。但是二者不匹配,因此scanf函数把空格放回原处,把字符·/·96留给下一次scanf函数调用来读取。为了允许第一个数后边有空格,应使用格式串"%d /%d"。
3.2.3 易混淆的printf函数和scanf函数
虽然scanf函数调用和printf函数调用看起来很相似,但这两个函数之间有很大的差异,忽略这些差异就是拿程序的正确性来冒险。
一个常见的错误是执行printf函数调用时在变量前面放置&。
printf("%d %d\n", &i, &j); /*** WRONG ***/
幸运的是,这种错误是很容易发现的:printf函数将显示一对样子奇怪的数,而不是变量i和j的值。
在寻找数据项时,scanf函数通常会跳过空白字符。因此除了转换说明,格式串通常不需要包含字符。另一个常见错误是假定scanf格式串应该类似于printf格式串,这种不正确的假定可能引发scanf函数行为异常。我们来看一下执行下面这个scanf函数调用时,到底发生了什么:
scanf("%d, %d", &i, &j);
scanf函数首先寻找输入中的整数,把这个整数存入变量i中;然后,scanf函数将试图把逗号与下一个输入字符相匹配。如果下一个输入的字符是空格而不是逗号,那么scanf函数将终止操作,而不再读取变量j的值。
printf格式串经常以\n结尾,但是在scanf格式串末尾放置换行符通常是一个坏主意。对scanf函数来说,格式串中的换行符等价于空格,两者都会导致scanf函数提前进入下一个非空白字符。例如,如果格式串是"%d\n",那么scanf函数将跳过空白字符,读取一个整数,然后跳到下一个非空白字符处。像这样的格式串可能会导致交互式程序一直“挂起”,直到用户输入一个非空白字符为止。
程序 分数相加
为了显示scanf函数的模式匹配能力,考虑读入由用户输入的分数。分数通常的形式为分子/分母。scanf函数允许读入整个分数,而不用将分子和分母视为两个整数分别读入。下面的分数相加程序体现了这一方法。
addfrac.c
/* Adds two fractions */ #include <stdio.h> int main(void) { int num1, denom1, num2, denom2, result_num, result_denom; printf("Enter first fraction: "); scanf("%d/%d", &num1, &denom1); printf("Enter second fraction: "); scanf("%d/%d", &num2, &denom2); result_num = num1 * denom2 + num2 * denom1; result_denom = denom1 * denom2; printf(`"`The sum is %d/%d\n`"`, result_num, result_denom); return 0; }
运行这个程序,可能的显示如下:
Enter first fraction: 5/6 Enter second fraction: 3/4 The sum is 38/24
注意,结果并没有化为最简分数。
问与答
* 问:转换说明%i也可以用于读写整数。%i和%d之间有什么区别?(p.29)
答:在printf格式串中使用时,二者没有区别。但是,在scanf格式串中,%d只能与十进制(基数为10)形式的整数相匹配,而%i则可以匹配用八进制(基数为8)、十进制或十六进制(基数为16)表示的整数。如果输入的数有前缀0(如056),那么%i会把它作为八进制数(➤7.1节)来处理;如果输入的数有前缀0x或0X(如0x56),那么%i会把它作为十六进制数(➤7.1节)来处理。如果用户意外地将0放在数的开始处,那么用%i代替%d读取数可能有意想不到的结果。因为这是一个陷阱,所以建议坚持采用%d。
问:如果printf函数将%作为转换说明的开始,那么如何显示字符%呢?
答:如果printf函数在格式串中遇到两个连续的字符%,那么它将显示出一个字符%。例如,语句
printf("Net profit: %d%%\n", profit);
可以显示出
Net profit: 10%
问:转义序列\t会使printf函数跳到下一个水平制表符处。如何知道水平制表符到底跳多远呢?(p.31)
答:不可能知道。打印\t的效果不是由C语言定义的,而是依赖于所使用的操作系统。水平制表符之间的距离通常是8个字符宽度,但C语言本身无法保证这一点。
问:如果要求读入一个数,而用户录入了非数值的输入,那么scanf函数会如何处理?
答:请看下面的例子:
printf("Enter a number: "); scanf("%d", &i);
假设用户录入了一个有效数,后边跟着一些非数值的字符:
Enter a number: 23foo
这种情况下,scanf函数读取2和3,并且将23存储在变量i中,而剩下的字符(foo)则留给下一次scanf函数调用(或者某些其他的输入函数)来读取。另外,假设输入从开始就是无效的:
Enter a number: foo
这种情况下,没有值会被存储到变量i中,字符foo会留给下一次scanf函数调用。
如何处理这种糟糕的情况呢?后面将看到检测scanf函数调用是否成功(➤22.3节)的方法。如果调用失败,可以终止或者尝试恢复程序,可能的方法是丢掉有问题的输入并要求用户重新输入。(在第22章结尾的“问与答”部分会讨论有关丢弃错误输入的方法。)
问:我不能理解scanf函数如何把字符“放回原处”并在以后再次读取。(p.33)
答:我们知道,用户从键盘输入时,程序并没有读取输入,而是把用户的输入放在一个隐藏的缓冲区中,由scanf函数来读取。scanf函数把字符放回到缓冲区中供后续读取是非常容易的。第22章会更详细地讨论输入缓冲。
问:如果用户在两个数之间加入了标点符号(如逗号),scanf函数将如何处理?
答:先来看一个简单的例子。假设我们想用scanf函数读取一对整数:
printf("Enter two numbers: "); scanf("%d%d", &i, &j);
如果用户输入
4,28
scanf函数将读取4并且把它存储在变量i中。在寻找第二个数的起始位置时,scanf函数遇到了逗号。因为数不能以逗号开头,所以scanf函数立刻返回,而把逗号和第二个数留给下一次scanf函数调用。
当然,如果能确定数与数之间始终用逗号来分隔,我们可以很容易地解决这个问题,只要在格式串中添加逗号即可:
printf("Enter two numbers, separated by a comma: "); scanf("%d,%d", &i, &j);
练习题1
1用\*标注的练习题比较棘手,正确答案往往不是显而易见的。仔细研读问题,必要时回顾一下相关章节的内容,一定要小心!
3.1节
1. 下面的printf函数调用产生的输出分别是什么?
(a) printf("%6d,%4d", 86, 1040);
(b) printf("%12.5e", 30.253);
(c) printf("%.4f", 83.162);
(d) printf("%-6.2g", .0000009979);
2. 编写printf函数调用,以下列格式显示float型变量x。
(a) 指数表示形式,栏宽8,左对齐,小数点后保留1位数字。
(b) 指数表示形式,栏宽10,右对齐,小数点后保留6位数字。
(c) 定点十进制表示形式,栏宽8,左对齐,小数点后保留3位数字。
(d) 定点十进制表示形式,栏宽6,右对齐,小数点后无数字。
3.2节
3. 说明下列每对scanf格式串是否等价?如果不等价,请指出它们的差异。
(a) "%d"与" %d"。
(b) "%d-%d-%d"与"%d -%d -%d"。
(c) "%f"与"%f "。
(d) "%f,%f"与"%f, %f"。
* 4. 假设scanf函数调用的格式如下:
scanf("%d%f%d", &i, &x, &j);
如果用户输入
10.3 5 6
调用执行后,变量i、x和j的值分别是多少?(假设变量i和变量j都是int型,变量x是float型。)
* 5. 假设scanf函数调用的格式如下:
scanf("%f%d%f", &x, &i, &y);
如果用户输入
12.3 45.6 789
调用执行后,变量x、i和y的值分别是多少?(假设变量x和变量y都是float型,变量i是int型。)
6. 指出如何修改3.2节中的addfrac.c程序,使用户可以输入在字符/的前后都有空格的分数。
编程题
1. 编写一个程序,以月/日/年(即mm/dd/yy)的格式接受用户输入的日期信息,并以年月日(即yyyymmdd)的格式将其显示出来:
Enter a date (mm/dd/yyyy): 2/17/2011 You entered the date 20110217
2. 编写一个程序,对用户输入的产品信息进行格式化。程序会话应类似下面这样:
Enter item number: 583 Enter unit price: 13.5 Enter purchase date (mm/dd/yyyy): 10/24/2010 Item Unit Purchase Price Date 583 $ 13.50 10/24/2010
其中,产品编号和日期项采用左对齐方式,单位价格采用右对齐方式,允许美元金额最大取值为9999.99。提示:各个列使用制表符控制。
3. 图书用国际标准书号(ISBN)进行标识。2007年1月1日之后分配的ISBN包含13位数字(旧的ISBN使用10位数字),分为5组,如978-0-393-97950-3。第一组(GS1前缀)目前为978或979。第二组(组标识)指明语言或者原出版国及地区(如0和1用于讲英语的国家)。第三组(出版商编号)表示出版商(393是W. W. Norton出版社的编号)。第四组(产品编号)是由出版商分配的用于识别具体哪一本书的编号(97950)。ISBN的末尾是一个校验数字,用于验证前面数字的准确性。编写一个程序来分解用户输入的ISBN信息:
Enter ISBN: 978-0-393-97950-3 GS1 prefix: 978 Group identifier: 0 Publisher code: 393 Item number: 97950 Check digit: 3
注意:每组中数字的个数是可变的,不能认为每组的长度都与示例一样。用实际的ISBN值(通常放在书的封底和版权页上)测试你编写的程序。
4. 编写一个程序,提示用户以(xxx) xxx-xxxx的格式输入电话号码,并以xxx.xxx.xxxx的格式显示该号码:
Enter phone number [(xxx) xxx-xxxx]: (404) 817-6900 You entered 404.817.6900
5. 编写一个程序,要求用户(按任意次序)输入1~16的所有整数,然后用4×4矩阵的形式将它们显示出来,再计算出每行、每列和每条对角线上的和:
Enter the numbers from 1 to 16 in any order: 16 3 2 13 5 10 11 8 9 6 7 12 4 15 14 1 16 3 2 13 5 10 11 8 9 6 7 12 4 15 14 1 Row sums: 34 34 34 34 Column sums: 34 34 34 34 Diagonal sums: 34 34
如果行、列和对角线上的和都一样(如本例所示),则称这些数组成一个幻方(magic square)。这里给出的幻方出现于艺术家和数学家Albrecht Dürer创作于1514年的一幅画中。(注意,矩阵的最后一行中间的两个数给出了该画的创作年代。)
6. 修改3.2节的addfrac.c程序,使用户可以同时输入两个分数,中间用加号隔开:
Enter two fractions separated by a plus sign: 5/6+3/4 The sum is 38/24