第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);
小技巧: 我在项目中经常用%p来调试指针,看地址对不对。特别是嵌入式开发里,指针指飞了,用%p一打就知道问题在哪。

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的「反向操作」——它从键盘读取数据,存到变量里。但这里有个大坑,我当年踩过不止一次。

重要提醒: scanf的第二个参数开始,必须传变量的地址!不是变量本身!
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 知识体系总览

下面这张图帮你理清本章的知识结构,建议保存下来随时回顾。

C语言输入输出体系 printf 格式化输出 格式说明符 宽度控制 精度控制 转义字符 scanf 格式化输入 地址传递 缓冲区问题 输入匹配 错误处理 getchar/putchar 逐字符处理 EOF检测 缓冲区清空 嵌入式应用 核心要点 printf输出要匹配格式 | scanf输入要传地址 | getchar返回int类型 ⚠ 常见陷阱 scanf忘记加& | 缓冲区残留换行符 | 字符输入被跳过

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的威力。

避坑指南: 我曾经在写类似程序时,忘了处理除零的情况。结果用户输入"5/0",程序直接崩溃了。从那以后,我写任何除法都会先检查除数是否为0。这个习惯救了我很多次。

6.6 总结

今天咱们把C语言的输入输出彻底过了一遍。printf和scanf是格式化I/O的主力,getchar和putchar是字符级I/O的基础。记住几个关键点:

  • printf的格式说明符要跟变量类型匹配,否则输出会乱码
  • scanf必须传地址,字符串除外
  • getchar返回int,不是char,为了能检测EOF
  • 缓冲区问题很常见,记得用空格或getchar清掉换行符

这些知识看着简单,但实际项目中出问题最多的往往就是这些基础操作。我见过太多人因为scanf没加&而debug一下午。嗯,希望你能少走这些弯路。


专注资料整理