C: 输入与输出
输入输出就像人与电脑的对话——printf是电脑对你说话,scanf是你对电脑说话,说清楚格式才不会鸡同鸭讲。
1. printf 格式化输出
printf 的名字来自 "print formatted"(格式化打印)。它的第一个参数是格式字符串,后面跟上要输出的值。
(1) 基本格式控制符
| 控制符 | 用途 | 示例 | 输出 |
|---|---|---|---|
%d |
十进制整数 | printf("%d", 42) |
42 |
%f |
浮点数 | printf("%f", 3.14) |
3.140000 |
%c |
单个字符 | printf("%c", 'A') |
A |
%s |
字符串 | printf("%s", "hi") |
hi |
%x |
十六进制 | printf("%x", 255) |
ff |
%o |
八进制 | printf("%o", 8) |
10 |
%p |
指针地址 | printf("%p", &a) |
地址值 |
%% |
百分号本身 | printf("100%%") |
100% |
C
#include <stdio.h>
int main(void) {
int age = 25;
float height = 175.5f;
char grade = 'A';
printf("年龄: %d, 身高: %.1f, 等级: %c\n", age, height, grade);
return 0;
}
TEXT
📖 仅展示
年龄: 25, 身高: 175.5, 等级: A
(2) 宽度与对齐
在 % 和控制符之间可以插入数字来控制输出宽度:
%5d:至少占5个字符宽度,右对齐%-5d:至少占5个字符宽度,左对齐%05d:不足5位用0填充
C
#include <stdio.h>
int main(void) {
int num = 42;
printf("[%5d]\n", num);
printf("[%-5d]\n", num);
printf("[%05d]\n", num);
printf("[%+5d]\n", num);
return 0;
}
TEXT
📖 仅展示
[ 42]
[42 ]
[00042]
[ +42]
(3) 浮点数精度
%.2f:小数点后保留2位%8.2f:总共至少8个字符宽,小数点后2位%-8.2f:左对齐版本
C
#include <stdio.h>
int main(void) {
double pi = 3.14159265358979;
printf("默认: %f\n", pi);
printf("2位: %.2f\n", pi);
printf("6位: %.6f\n", pi);
printf("宽8位2: %8.2f\n", pi);
return 0;
}
TEXT
📖 仅展示
默认: 3.141593
2位: 3.14
6位: 3.141593
宽8位2: 3.14
💡
%f 默认保留6位小数。%.0f 可以不显示小数部分。
2. scanf 读取输入
scanf 的名字来自 "scan formatted"(格式化扫描)。它按照格式字符串的指定,从标准输入读取数据并存入变量。
(1) 基本用法
C
#include <stdio.h>
int main(void) {
int age;
float score;
printf("请输入年龄: ");
scanf("%d", &age);
printf("请输入成绩: ");
scanf("%f", &score);
printf("年龄: %d, 成绩: %.1f\n", age, score);
return 0;
}
TEXT
📖 仅展示
请输入年龄: 20
请输入成绩: 92.5
年龄: 20, 成绩: 92.5
⚠️
scanf 的变量参数必须加 &(取地址符),表示"把值存到这个地址"。忘加 & 是初学者最常见的错误,程序不会报错但会崩溃。
(2) 读取多个值
C
#include <stdio.h>
int main(void) {
int a, b, c;
printf("输入三个整数(空格分隔): ");
scanf("%d %d %d", &a, &b, &c);
printf("你输入了: %d, %d, %d\n", a, b, c);
return 0;
}
TEXT
📖 仅展示
输入三个整数(空格分隔): 10 20 30
你输入了: 10, 20, 30
scanf 默认用空白(空格、回车、Tab)分隔不同数据。
(3) 读取字符和字符串
C
#include <stdio.h>
int main(void) {
char ch;
char name[20];
printf("输入一个字符: ");
scanf(" %c", &ch);
printf("输入姓名(无空格): ");
scanf("%s", name);
printf("字符: %c, 姓名: %s\n", ch, name);
return 0;
}
⚠️
%c 前面加一个空格 " %c" 可以跳过空白字符,否则它会读取上一次留下的回车。%s 读取字符串时不包含空格,遇到空格就停止。name 是数组名,本身就是地址,不需要加 &。
(4) scanf 的返回值
scanf 返回成功读取的变量个数,这个特性可以用来检测输入是否合法:
C
#include <stdio.h>
int main(void) {
int num;
printf("请输入一个整数: ");
int result = scanf("%d", &num);
if (result == 1) {
printf("读取成功: %d\n", num);
} else {
printf("输入无效!\n");
}
return 0;
}
TEXT
📖 仅展示
请输入一个整数: abc
输入无效!
3. getchar 与 putchar
getchar() 从键盘读取一个字符,putchar() 向屏幕输出一个字符。它们比 scanf("%c") 和 printf("%c") 更简洁高效。
C
#include <stdio.h>
int main(void) {
char ch;
printf("请输入一个字符: ");
ch = getchar();
printf("你输入了: ");
putchar(ch);
putchar('\n');
return 0;
}
TEXT
📖 仅展示
请输入一个字符: X
你输入了: X
(1) 读取一整行
getchar() 配合循环可以读取一整行,直到遇到换行符:
C
#include <stdio.h>
int main(void) {
char ch;
printf("请输入一行文字: ");
while ((ch = getchar()) != '\n') {
putchar(ch);
}
putchar('\n');
return 0;
}
TEXT
📖 仅展示
请输入一行文字: Hello World!
Hello World!
💡
while ((ch = getchar()) != '\n') 是一个非常经典的写法:先读取字符赋给ch,再判断是否为换行。注意外层括号不能省。
4. 缓冲区问题
C语言的标准输入使用行缓冲:用户敲回车后,整行数据才送入缓冲区供程序读取。这会导致一些令人困惑的问题。
(1) 问题:scanf %c 读取残留回车
C
#include <stdio.h>
int main(void) {
int num;
char ch;
printf("输入整数: ");
scanf("%d", &num);
printf("输入字符: ");
scanf("%c", &ch);
printf("整数=%d, 字符=%d\n", num, ch);
return 0;
}
TEXT
📖 仅展示
输入整数: 42
输入字符: 整数=42, 字符=10
输入 42 后按回车,缓冲区留下 42\n。scanf("%d") 读了42,scanf("%c") 直接读了那个 \n(ASCII码10),根本没等你输入字符。
(2) 解决方案
方案1:在 %c 前加空格(推荐)
C
scanf(" %c", &ch);
空格会跳过所有空白字符(包括回车、空格、Tab)。
方案2:用 getchar() 吸收残留
C
scanf("%d", &num);
getchar();
scanf("%c", &ch);
方案3:清空缓冲区
C
int c;
while ((c = getchar()) != '\n' && c != EOF) {}
⚠️ 不要用
fflush(stdin) 来清空输入缓冲区——这在标准C中是未定义行为,虽然某些编译器支持,但不可移植。
▶ 示例
编写一个程序,同时读取整数和字符,正确处理缓冲区残留:
C
#include <stdio.h>
int main(void) {
int age;
char gender;
printf("请输入年龄: ");
scanf("%d", &age);
printf("请输入性别(M/F): ");
scanf(" %c", &gender);
printf("年龄: %d, 性别: %c\n", age, gender);
return 0;
}
TEXT
📖 仅展示
请输入年龄: 25
请输入性别(M/F): M
年龄: 25, 性别: M
▶ 示例
用 getchar() 逐字符读取,统计一行中字母和数字的个数:
C
#include <stdio.h>
int main(void) {
char ch;
int letters = 0;
int digits = 0;
printf("请输入一行文字: ");
while ((ch = getchar()) != '\n') {
if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z') {
letters++;
} else if (ch >= '0' && ch <= '9') {
digits++;
}
}
printf("字母: %d个, 数字: %d个\n", letters, digits);
return 0;
}
TEXT
📖 仅展示
请输入一行文字: Hello 2024!
字母: 5个, 数字: 4个
5. puts 与 fputs
puts() 输出字符串并自动换行,比 printf 简单,适合只输出纯文本的场景:
C
#include <stdio.h>
int main(void) {
puts("你好,世界!");
puts("自动换行,无需加\\n");
return 0;
}
TEXT
📖 仅展示
你好,世界!
自动换行,无需加\n
💡
puts() 比 printf("%s\n", ...) 效率更高,因为不需要解析格式字符串。不需要格式化时优先用 puts。
❓ 常见问题
Q 为什么
scanf 的变量前面要加 &?A
scanf 需要知道把值存到哪里,& 取变量的地址。只有数组名不需要加,因为数组名本身就是首元素的地址。Q
scanf("%c") 跳过了我的输入怎么办?A 因为缓冲区里有上一次留下的回车。在
%c 前加一个空格 scanf(" %c", &ch) 即可跳过空白。Q
printf 和 scanf 的格式控制符可以用大写吗?A 部分可以。
%d 的小写对应 int,大写 %D 不标准。但 %x 的小写输出 abcdef,大写 %X 输出 ABCDEF,这是合法的。Q 如何安全地读取带空格的字符串?
A
scanf("%s") 遇到空格就停止。可以用 fgets(name, sizeof(name), stdin) 读取整行,它支持空格且更安全。📖 小节
printf用格式控制符控制输出:宽度、精度、对齐、填充scanf读取输入时变量必须加&,%c前加空格避免读残留回车getchar/putchar是单字符读写的高效选择- 行缓冲导致
scanf和getchar之间容易产生残留数据问题 scanf返回值可用来检测输入是否合法
📝 作业
- 编写程序,输入圆的半径,用
printf的%.2f格式输出圆的面积和周长。 - 编写程序,用
getchar()读取一行输入,统计其中大写字母、小写字母各多少个。 - 编写程序,输入一个整数,分别用
%d、%x、%o输出它的十进制、十六进制和八进制形式。