第三章 格式化输入/输出《C语言程序设计现代方法(第2版)》读书笔记

简介: 第三章 格式化输入/输出《C语言程序设计现代方法(第2版)》读书笔记

第三章 格式化输入/输出 🚀


scanf函数和printf函数是C语言编程中使用最频繁的两个函数,它们用来格式化输入和输

出。



3.1 printf 函数🚀

  printf函数被设计用来显示 格式串 ( format string )的内容,并且在该串中的指定位置插

入可能的值。调用 printf 函数时必须提供格式串,格式串后面的参数是需要在显示时插入到

该串中的值:

       printf(格式串 , 表达式 1, 表达式 2, ...);

显示的值可以是常量、变量或者更加复杂的表达式。调用 printf 函数一次可以打印的值的个数

没有限制。

       格式串包含普通字符和转换说明 ( conversion specification ),其中转换说明以字符 % 开头。转换说明是用来表示打印过程中待填充的值的占位符。跟随在字符% 后边的信息指定了把数值从内部形式(二进制)转换 成打印形式(字符)的方法,这也就是“转换说明”这一术语的由来。例如,转换说明%d 指定 printf 函数把 int 型值从二进制形式转换成十进制数字组成的字符串,转换说明%f 对 float 型值也进行类似的转换。


注意:

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 转换说明🚀


转换说明可以用%m.pX格式或%-m.pX格式


最小栏宽 ( minimum field width ) m 指定了要显示的最少字符数量。如果要显示的数值所

需的字符数少于 m ,那么值在字段内是右对齐的。(换句话说,在值前面放置额外的空格。)例如,转换说明%4d 将以 · 123 的形式显示数 123 (本章用符号 · 表示空格字符)。如果要显示的值所需的字符数多于m ,那么字段宽度会自动扩展为所需的尺寸。因此,转换说明 %4d 将以 12345的形式显示数12345 ,而不会丢失数字。在 m 前放上一个负号会导致左对齐;转换说明 %-4d 将以123· 的形式显示 123 。

精度 ( precision ) p 的含义很难描述,因为它依赖于 转换说明符 ( conversion specifier ) X 的选择。X 表明在显示数值前需要对其进行哪种转换。对数值来说最常用的转换说明符有以下几个。


image.png


编写程序时无法预知数的大小或者数值变化范围很大的情况下,说明符 g 对于数的显示是特

别有用的。在用于显示大小适中的数时,说明符 g 采用定点十进制形式。但是,在显示非常大或非常小的数时,说明符g 会转换成指数形式以便减少所需的字符数。


3.1.2 转义序列🚀

格式串中常用的代码\n被称为转义序列escape sequence)。转义序列( 7.3节)使字符串包含一些特殊字符而不会使编译器引发问题,这些字符包括非打印的(控制)字符和对编译器有 特殊含义的字符(如 " 


image.png


当这些转义序列出现在 printf 函数的格式串中时,它们表示在显示中执行的操作。在大多

数机器上,输出 \a 会产生一声鸣响,输出 \b 会使光标从当前位置回退一个位置,输出 \n 会使光

标跳到下一行的起始位置,输出 \t 会把光标移动到下一个制表符的位置。

附带提一下,不能在字符串中只放置单独一个字符 \ ,编译器将认为它是一个转义序列的开

始。为了显示单独一个字符 \ ,需要在字符串中放置两个 \ 字符:

printf("\\"); /* prints one \ character */


3.2 scanf 函数🚀

scanf函数转换说明的用法和printf函数转换说明的用法本质上是一样的。

scanf 函数调用中像 "%d%d%f%f" 这样“紧密压缩”的格式串是很普遍的,而printf 函数的格式串很少有这样紧挨着的转换说明。


image.png


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 表示此项被读取为输入项的一部分:


image.png


scanf函数“忽略”了最后的换行符,实际上没有读取它。这个换行符将是下一次scanf函数调

用的第一个字符。

scanf 函数遵循什么规则来识别整数或浮点数呢?在要求读入整数时, scanf 函数首先寻找正号或负号,然后读取数字直到读到一个非数字时才停止。当要求读入浮点数时,scanf函数

会寻找一个正号或负号(可选),随后是一串数字(可能含有小数点),再后是一个指数(可选)。指数由字母e (或者字母 E )、可选的符号和一个或多个数字构成。在用于 scanf 函数时,转换说明%e 、 %f 和 %g 是可以互换的,这 3 种转换说明在识别浮点数方面都遵循相同的规则。

当scanf函数遇到一个不可能属于当前项的字符时,它会把此字符“放回原处”,以便在扫描下一个输入项或者下一次调用scanf函数时再次读入。 思考下面(公认有问题的) 4 个数的排列:


image.png

image.png


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 函数🚀


image.png


问与答(很重要)🚀

*问:转换说明%i也可以用于读写整数。%i%d之间有什么区别?p.27

* 答:在 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.29 )

* 答:不可能知道。打印 \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.31)


答:我们知道,用户从键盘输入时,程序并没有读取输入,而是把用户的输入放在一个隐藏的缓冲区中,由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);

相关文章
|
3天前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
41 16
|
7天前
|
算法 C语言
【C语言程序设计——循环程序设计】求解最大公约数(头歌实践教学平台习题)【合集】
采用欧几里得算法(EuclideanAlgorithm)求解两个正整数的最大公约数。的最大公约数,然后检查最大公约数是否大于1。如果是,就返回1,表示。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。作为新的参数传递进去。这个递归过程会不断进行,直到。有除1以外的公约数;变为0,此时就找到了最大公约数。开始你的任务吧,祝你成功!是否为0,如果是,那么。就是最大公约数,直接返回。
56 18
|
7天前
|
Serverless C语言
【C语言程序设计——循环程序设计】利用循环求数值 x 的平方根(头歌实践教学平台习题)【合集】
根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码,求解出数值x的平方根;运用迭代公式,编写一个循环程序,求解出数值x的平方根。注意:不能直接用平方根公式/函数求解本题!开始你的任务吧,祝你成功!​ 相关知识 求平方根的迭代公式 绝对值函数fabs() 循环语句 一、求平方根的迭代公式 1.原理 在C语言中,求一个数的平方根可以使用牛顿迭代法。对于方程(为要求平方根的数),设是的第n次近似值,牛顿迭代公式为。 其基本思想是从一个初始近似值开始,通过不断迭代这个公式,使得越来越接近。
37 18
|
7天前
|
C语言
【C语言程序设计——循环程序设计】统计海军鸣放礼炮声数量(头歌实践教学平台习题)【合集】
有A、B、C三艘军舰同时开始鸣放礼炮各21响。已知A舰每隔5秒1次,B舰每隔6秒放1次,C舰每隔7秒放1次。编程计算观众总共听到几次礼炮声。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。开始你的任务吧,祝你成功!
40 13
|
2天前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
12 3
|
7天前
|
存储 安全 C语言
【C语言程序设计——选择结构程序设计】预测你的身高(头歌实践教学平台习题)【合集】
分支的语句,这可能不是预期的行为,这种现象被称为“case穿透”,在某些特定情况下可以利用这一特性来简化代码,但在大多数情况下,需要谨慎使用。编写一个程序,该程序需输入个人数据,进而预测其成年后的身高。根据提示,在右侧编辑器补充代码,计算并输出最终预测的身高。分支下的语句,提示用户输入无效。常量的值必须是唯一的,且在同一个。语句的作用至关重要,如果遗漏。开始你的任务吧,祝你成功!,程序将会继续执行下一个。常量都不匹配,就会执行。来确保程序的正确性。
30 10
|
7天前
|
小程序 C语言
【C语言程序设计——基础】顺序结构程序设计(头歌实践教学平台习题)【合集】
目录 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果: 任务描述 相关知识 编程编写一个程序,从键盘输入3个变量的值,例如a=5,b=6,c=7,然后将3个变量的值进行交换,使得a=6,b=7,c=5。面积=sqrt(s(s−a)(s−b)(s−c)),s=(a+b+c)/2。使用输入函数获取半径,格式指示符与数据类型一致,实验一下,不一致会如何。根据提示,在右侧编辑器补充代码,计算并输出圆的周长和面积。
27 10
|
2天前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
8 2
|
7天前
|
存储 C语言
【C语言程序设计——循环程序设计】利用数列的累加和求 sinx(头歌实践教学平台习题)【合集】
项的累加和,一般会使用循环结构,在每次循环中计算出当前项的值(可能基于通项公式或者递推关系),然后累加到一个用于存储累加和的变量中。在C语言中推导数列中的某一项,通常需要依据数列给定的通项公式或者前后项之间的递推关系来实现。例如,对于一个简单的等差数列,其通项公式为。的级数,其每一项之间存在特定的递推关系(后项的分子是其前项的分子乘上。,计算sinx的值,直到最后一项的绝对值小于。为项数),就可以通过代码来计算出指定项的值。对于更复杂的数列,像题目中涉及的用于近似计算。开始你的任务吧,祝你成功!
26 6
|
6天前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
34 1

热门文章

最新文章

相关实验场景

更多