第6章:输入与输出——scanf与printf的格式化输入输出,getchar与putchar的使用
说到C语言的输入输出,这可能是你每天打交道最多的东西了。我个人习惯把这一章叫做「C语言的嘴巴和耳朵」——printf是嘴巴,负责把数据说出来;scanf是耳朵,负责听用户输入。而getchar和putchar呢,就是更基础的「单字」交流方式。
嗯,咱们今天就把这些东西彻底讲透。我保证,学完这一章,你写代码时再也不会被输入输出卡脖子。
6.1 printf:格式化输出
printf这玩意儿,说白了就是「按指定格式打印」。它的原型在stdio.h里,你每次用之前都得记得#include一下。
#include <stdio.h>
int main() {
int age = 25;
float height = 1.78;
char grade = 'A';
printf("年龄:%d,身高:%.2f米,成绩:%c\n", age, height, grade);
return 0;
}
这里面的%d、%.2f、%c就是格式占位符。我刚开始学的时候老记混,后来自己总结了个口诀:「d是整数,f是浮点,c是字符,s是字符串」。你想想看,是不是好记多了?
6.1.1 常用格式说明符
| 格式符 | 含义 | 示例 |
|---|---|---|
| %d | 有符号十进制整数 | printf("%d", 100); |
| %u | 无符号十进制整数 | printf("%u", 200); |
| %f | 浮点数(默认6位小数) | printf("%f", 3.14); |
| %.2f | 保留两位小数的浮点数 | printf("%.2f", 3.14159); |
| %c | 单个字符 | printf("%c", 'A'); |
| %s | 字符串 | printf("%s", "Hello"); |
| %x | 十六进制(小写) | printf("%x", 255); |
| %p | 指针地址 | printf("%p", &age); |
6.1.2 宽度与精度控制
有时候你想让输出对齐,比如打印一个表格。这时候宽度控制就派上用场了。
int a = 5, b = 123, c = 4567;
printf("%4d\n", a); // 输出:" 5"(右对齐,占4位)
printf("%-4d\n", b); // 输出:"123 "(左对齐,占4位)
printf("%04d\n", c); // 输出:"4567"(前面补0)
精度控制主要用在浮点数上。%.2f就是保留两位小数,%.3f就是三位。我记得有一次做传感器数据采集,要求精度到小数点后三位,我就是用%.3f搞定的。
6.2 scanf:格式化输入
scanf是printf的「反向操作」——它从键盘读取数据,存到变量里。但这里有个大坑,我当年踩过不止一次。
int age;
float height;
char name[50];
printf("请输入年龄:");
scanf("%d", &age); // 注意&符号
printf("请输入身高:");
scanf("%f", &height); // 注意&符号
printf("请输入姓名:");
scanf("%s", name); // 数组名本身就是地址,不需要&
为什么会这样?因为scanf需要知道「把数据写到哪里去」。你传变量本身,它只知道值,不知道地址。传&age,它才知道内存位置。嗯,这里要特别注意,字符串数组名本身就是地址,所以不用加&。
6.2.1 scanf的常见问题
我曾经在项目里遇到过一个诡异的问题:用户输入完一个整数后,按了回车,结果下一个字符输入直接跳过了。后来才发现,是缓冲区里残留的换行符搞的鬼。
int num;
char ch;
printf("输入一个数字:");
scanf("%d", &num);
// 这里有个坑!缓冲区里还留着'\n'
printf("输入一个字符:");
scanf("%c", &ch); // 这个ch会直接读到'\n',而不是你想要的字符
解决办法很简单:在%c前面加个空格,或者用getchar吃掉那个换行符。
// 方法1:加空格
scanf(" %c", &ch); // 空格会跳过空白字符
// 方法2:吃掉换行
getchar(); // 吃掉缓冲区里的'\n'
scanf("%c", &ch);
6.3 getchar与putchar:字符级输入输出
这两个函数是C语言里最基础的I/O操作。getchar从标准输入读一个字符,putchar向标准输出写一个字符。说白了,就是一次只处理一个字符。
char c;
printf("请输入一个字符:");
c = getchar(); // 读取一个字符
putchar(c); // 输出这个字符
putchar('\n'); // 输出换行
你可能会问:「这有啥用?printf和scanf不是更方便吗?」
我告诉你,在嵌入式开发里,getchar和putchar特别有用。比如串口通信,很多时候就是一次收发一个字节。用printf反而显得笨重。我在做STM32项目时,底层驱动就是基于putchar实现的printf重定向。
6.3.1 getchar的返回值
这里有个细节很多人会忽略:getchar返回的是int类型,不是char。为什么?因为它要能返回EOF(文件结束标志)。EOF通常是-1,而char可能是无符号的,存不下-1。
int c; // 注意是int,不是char
while ((c = getchar()) != EOF) {
putchar(c);
}
这个模式在读取文件或标准输入时非常常见。我写命令行工具时经常用这个循环,逐字符处理输入流。
6.4 知识体系总览
下面这张图帮你理清本章的知识结构,建议保存下来随时回顾。
6.5 综合示例:一个简单的计算器
咱们把今天学的知识串起来,写一个简单的计算器程序。这个程序支持加减乘除,用户输入两个数和运算符,程序输出结果。
#include <stdio.h>
int main() {
double num1, num2, result;
char op;
printf("请输入表达式(如:3.5 + 2.1):\n");
scanf("%lf %c %lf", &num1, &op, &num2);
switch(op) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if (num2 != 0) {
result = num1 / num2;
} else {
printf("错误:除数不能为0!\n");
return 1;
}
break;
default:
printf("错误:不支持的运算符!\n");
return 1;
}
printf("%.2f %c %.2f = %.2f\n", num1, op, num2, result);
return 0;
}
这个例子用到了scanf的格式化输入、printf的格式化输出,还有switch分支判断。你想想看,如果不用格式化输入,你得写多少代码来解析用户输入?这就是scanf的威力。
6.6 总结
今天咱们把C语言的输入输出彻底过了一遍。printf和scanf是格式化I/O的主力,getchar和putchar是字符级I/O的基础。记住几个关键点:
- printf的格式说明符要跟变量类型匹配,否则输出会乱码
- scanf必须传地址,字符串除外
- getchar返回int,不是char,为了能检测EOF
- 缓冲区问题很常见,记得用空格或getchar清掉换行符
这些知识看着简单,但实际项目中出问题最多的往往就是这些基础操作。我见过太多人因为scanf没加&而debug一下午。嗯,希望你能少走这些弯路。