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) 宽度与对齐

% 和控制符之间可以插入数字来控制输出宽度:

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) 浮点数精度

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\nscanf("%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 printfscanf 的格式控制符可以用大写吗?
A 部分可以。%d 的小写对应 int,大写 %D 不标准。但 %x 的小写输出 abcdef,大写 %X 输出 ABCDEF,这是合法的。
Q 如何安全地读取带空格的字符串?
A scanf("%s") 遇到空格就停止。可以用 fgets(name, sizeof(name), stdin) 读取整行,它支持空格且更安全。

📖 小节

📝 作业

  1. 编写程序,输入圆的半径,用 printf%.2f 格式输出圆的面积和周长。
  2. 编写程序,用 getchar() 读取一行输入,统计其中大写字母、小写字母各多少个。
  3. 编写程序,输入一个整数,分别用 %d%x%o 输出它的十进制、十六进制和八进制形式。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏