输出: printf与转换说明
转义序列
输入: scanf
getchar与putchar
综合案例: 分数相加
printf 函数必须提供格式字符串, 后跟打印期间要插入到串中的任何值:
printf(字符串, 表达式1, 表达式2, ...);
格式字符串可以包含普通字符和转换说明, 以 % 字符开头
转换说明 是一个占位符, 表示要在打印期间填充的值
%d 用于 int 值
%f 用于 float / double 值 (printf 中 float 自动提升为 double)
%c 用于 char 值
%s 用于 char [] 值
格式字符串:
普通字符 按出现在字符串中的方式打印
转换说明 则需用待显示的值来替换填充
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);
输出:
i = 10, j = 20, x = 43.289200, y = 5527.000000
编译器不检查格式串与输出项是否匹配:
printf("%d %d\n", i); /*** WRONG: 转换说明过多 ***/
printf("%d\n", i, j); /*** WRONG: 转换说明太少 ***/
也不检查转换说明是否合适; 用错了 → 无意义的输出
int i;
float x;
printf("%f %d\n", i, x); /*** WRONG ***/
%d 配 float/double 时, 读到的不是这个数的比特: 整数和浮点数调用时走不同的通道, %d 在整数通道里拿到的只是残留的无关值 (第 3 周细讲)
转换说明可以采用 %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
最小字段宽度 m 指定要打印的最小字符数
如果要打印的值需要少于 m 个字符, 则在字段内右对齐
转换说明 %4d 会将数字 123 显示为 •123(• 表示空格字符)
如果要打印的值需要超过 m 个字符, 则字段宽度会自动扩展为所需的大小, 若 m 前面加上减号会导致左对齐
转换说明 %-4d 会将数字 123 显示为 123•
精度 p 的含义取决于转换说明符 X 的选择
结合 %d 说明符使用, %m.pX 中的 p 表示要显示的最小位数(如需要, 将在数字的开头添加额外的零)
如果 p 省略, 则默认它的值为 1
浮点数的转换说明:
%e 指数格式: p 表示小数点后位数 (默认 6; p 为 0 不显示小数点)
%f 固定十进制格式: p 含义与 %e 相同
%g 按数字大小自动选指数或定点: p 表示最大有效位数, 不显示尾随零
/* 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;
}
tprintf.c程序使用 printf 以各种格式显示整数和浮点数
输出:
|40|•••40|40•••|••040|
|•••839.210|•8.392e+02|839.21••••|
形如 \n 的代码称为 转义序列
转义序列: 使字符串包含非打印(控制)字符和具有特殊含义的字符(如 ")
转义序列的部分列表:
| 警报(铃声) | \a |
|---|---|
| 回退符 | \b |
| 换行符 | \n |
| 水平制表符 | \t |
一个字符串可以包含任意数量的转义序列:
printf("Item\tUnit\tPurchase\n\tPrice\tDate\n");
另一常见的转义序列是 \", 代表 " 字符:
printf("\"Hello!\""); /* prints "Hello!" */
要打印单个 \ 字符, 在字符串中放置两个 \:
printf("\\"); /* prints one \ character */
scanf 根据指定格式读取输入; 格式字符串同样含普通字符与转换说明, 与 printf 基本相同
在多数情况下, scanf 格式字符串只包含转换说明:
int i, j;
float x, y;
scanf("%d%d%f%f", &i, &j, &x, &y);
样本输入:
1 -20 .3 -4.0e3
scanf 将变量 i、j、x和y赋值为 1、–20、0.3 和 –4000.0
使用 scanf 时, 程序员须自己检查:
转换说明 的数量与 输入变量 的数量是否相同
确保各个转换说明 适用于 相应变量
& 符号通常需要用在 scanf 调用中的每个变量之前
& 通常(但不总是)是必需的, 在读取输入至变量时需记住使用它
scanf 尝试将输入字符组与格式字符串中的转换说明进行匹配
对每个转换说明, scanf 尝试在输入数据中寻找适当类型数据, 必要时跳过空格
当被要求读取整数时, 首先搜索数字或正负号, 然后继续读取数字, 直到遇到非数字
遇到不能成为当前变量一部分的字符时, 会 放回 该字符留待下次读取
如果数据读取不成功, scanf 立即返回
输入流中没有可用的数据时, scanf 会等待用户输入——交互式程序"卡住"往往就是在等你敲键盘
scanf 搜索数字时忽略空白 (空格/制表/换行); 读取四个数字的调用:
scanf("%d%d%f%f", &i, &j, &x, &y);
输入的数字可以在一行上, 也可以分布在多行上:
••1
-20•••.3
•••-4.0e3
样本输入 1-20.3-4.0e3¤ 的处理步骤:
%d 把 1 存进 i、把 -20 存进 j (分别放回 - 和 .)
%f 把 0.3 存进 x、把 -4.0×10³ 存进 y (放回 - 和换行符)
scanf 每次从上次读到的位置接着读, 程序里的多次调用共享同一条输入字符流:
int a, b;
printf("请输入两个整数: ");
scanf("%d", &a); /* 读走 10, 剩 " 20↵" */
printf("a = %d\n", a); /* 马上打印已读到的 */
scanf("%d", &b); /* 跳过空格, 接着读 20 */
printf("b = %d\n", b);
用户只输入一行 10 20↵:
请输入两个整数: 10 20↵
a = 10
b = 20
输入先全部进入缓冲区, 两条 scanf 依次"消费": 第一个只拿走 10, 第二个接着拿走 20
夹在中间的 printf 打印的是"已经读到的"——输入与输出互不干扰
scanf 读走的数据不会再次被读到(不回看、不重复)
试一试: 如果第一次只输入 10↵ 就回车, 会发生什么? 程序会卡住等待继续输入——第二个 scanf 在缓冲区里找不到整数
%d、%f 会自动跳过空格和换行; %c 不会——它读到的可能就是上一次输入留下的换行符
int n; char ch;
scanf("%d", &n);
scanf("%c", &ch); /* 读到的是 '\n' 或空格, 不是用户新输入的字符! */
解决: 在 %c 前加一个空格表示"跳过空白": scanf(" %c", &ch);
scanf 与 getchar 混用时的经典坑: scanf 结束后换行符还留在输入流里, 会被后面的 getchar() 当"第一个字符"吃掉
scanf("%d", &n);
getchar(); /* 先把遗留的换行符吃掉, 再读下面的字符 */
char name[100];
scanf("%s", name); /* 输入 Yang Yibiao↵ */
printf("hi, %s\n", name); /* hi, Yang */
char line[100];
getchar(); /* 先吃掉上一行遗留的 \n */
scanf("%[^\n]", line); /* 整行读入, 空格不停 */
printf("[%s]\n", line); /* [Hello World] */
字符串 = char 数组: char s[100]; 开得足够大, 结尾是 '\0'
scanf("%s", s): 不加 &; 遇空白(空格/Tab/换行)就停 → 只读一个"单词"
想读带空格的一整行: scanf("%[^\n]", s) —— 一直读到换行才停
坑: 上一行 scanf 读完后缓冲区遗留的换行会让 %[^\n] 立刻空手而归 → 先 getchar() 吃掉
输出: printf("%s", s); 单个字符用 %c
输入比数组长会溢出——为什么危险, 第 10 周讲指针时再说
当在格式字符串中遇到一个或多个空白字符时, scanf 从输入中读取空白字符, 直到它遇到一个非空白字符(即*放回*)
当它在格式字符串中遇到非空白字符时, scanf 将其与下一个输入字符进行比较
如果匹配, 则 scanf 丢弃输入字符并继续处理格式字符串
若不匹配, 则 scanf 会将有问题的字符放回输入中, 然后中止
例子:
如果格式字符串是 "%d/%d" 并且输入是 •5/•96, scanf 成功
如果输入 •5•/•96, 则 scanf 失败, 因为格式字符串中的 / 与输入中的空格不匹配
要在第一个数字后允许空格, 需改用格式字符串 "%d /%d"
scanf 和 printf 的调用看起来相似, 但两者之间存在显著差异
一个常见的错误是在printf调用中将&放在变量前面:
printf("%d %d\n", &i, &j); /*** WRONG ***/
不要错误地假设 scanf 格式字符串应该类似于 printf:
scanf("%d, %d", &i, &j);
若输入中逗号的位置实际是空格, 则scanf将终止而不读取j的值
多数情况下, 一定不要在 scanf 格式化字符串的末尾放置换行符, 否则会导致交互式程序 挂起 直到用户输入下一个非空格字符
getchar: 读取单个字符
int ch; /* 注意: 返回值是 int, 不是 char! */
ch = getchar(); /* 从键盘读取一个字符, 存入 ch */
为什么用 int? 因为 getchar 还可能返回 EOF, char 装不下
putchar: 输出单个字符
putchar(ch); /* 在屏幕上显示字符 ch */
putchar('\n'); /* 输出换行符 */
getchar/putchar 读写字符的效率高于 scanf/printf, 在字符处理类程序(如统计输入字符个数)中常用
#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
printf("%-8.3f", x) 中 -、8、.3 分别表示什么?
" 和 \,转义序列分别怎么写?
scanf("%d/%d", &a, &b) 要求用户怎么输入?
getchar() 返回值的类型是什么?为什么它适合读取字符?