C语言输入与输出

任何程序都离不开输入与输出(I/O)。C语言本身没有内置的输入输出语句,而是通过标准库函数来实现。 最常用的输入输出函数包括printf()(格式化输出)、scanf()(格式化输入)、 getchar() / putchar()(字符I/O)以及fgets() / puts()(字符串I/O)。 掌握这些函数是编写交互式程序的基础。

📤 printf 格式化输出

printf() 函数用于将数据按照指定的格式输出到标准输出(通常是屏幕)。其基本语法:

int printf(const char *format, ...);

返回值:成功时返回输出的字符数,失败时返回负数。

📌 格式说明符

3232
格式符输出类型示例
%d%i十进制整数printf("%d", 100);
%u无符号十进制整数printf("%u", 40000);
%o八进制整数printf("%o", 100); → 144
%x / %X十六进制整数(小写/大写)printf("%x", 100); → 64
%f浮点数(小数形式)printf("%f", 3.14); → 3.140000
%e / %E浮点数(科学计数法)printf("%e", 3.14); → 3.140000e+00
%g / %G根据值自动选择 %f 或 %eprintf("%g", 0.000123); → 0.000123
%c单个字符printf("%c", 'A');
%s字符串printf("%s", "Hello");
%p指针地址printf("%p", &x);
%%输出百分号printf("%%"); → %

🎨 格式修饰符

可以在 % 和格式符之间插入修饰符来控制输出的宽度、精度、对齐方式等:

  • 宽度%5d 输出至少占5个字符宽度,右对齐;%-5d 左对齐。
  • 精度%.2f 保留两位小数;%.5s 只输出字符串前5个字符。
  • 标志+ 显示正号;0 用0填充空白;# 显示进制前缀(如0x)。
#include <stdio.h>

int main() {
    int num = 123;
    double pi = 3.1415926;
    char str[] = "C Programming";

    printf("默认: %d\n", num);
    printf("宽度5: %5d\n", num);
    printf("左对齐: %-5d|\n", num);
    printf("补零: %05d\n", num);
    printf("精度2: %.2f\n", pi);
    printf("宽度8精度3: %8.3f\n", pi);
    printf("字符串截断: %.5s\n", str);
    printf("科学计数法: %e\n", pi);
    return 0;
}

📥 scanf 格式化输入

scanf() 函数用于从标准输入(键盘)读取数据并按照格式存储到变量中。基本语法:

int scanf(const char *format, ...);

返回值:成功读取并赋值的变量个数,若失败返回EOF(-1)。

重要: scanf 的第二个参数开始必须是变量的地址,对于普通变量需使用取地址符 &,对于指针或数组名则不用。

📌 常用格式说明符

printf类似,但scanf的格式说明符略有不同:

  • %d:读取十进制整数
  • %f:读取浮点数(float)
  • %lf:读取双精度浮点数(double)
  • %c:读取单个字符(包括空白字符)
  • %s:读取字符串,遇到空白字符停止
  • %x:读取十六进制整数
#include <stdio.h>

int main() {
    int age;
    float height;
    double weight;
    char name[50];

    printf("请输入年龄: ");
    scanf("%d", &age);

    printf("请输入身高(cm): ");
    scanf("%f", &height);

    printf("请输入体重(kg): ");
    scanf("%lf", &weight);   // 注意double用%lf

    printf("请输入姓名: ");
    scanf("%s", name);        // name是数组名,本身就是地址,不加&

    printf("姓名: %s, 年龄: %d, 身高: %.1fcm, 体重: %.2fkg\n", name, age, height, weight);
    return 0;
}
注意: scanf("%s", ...) 不会读取空格,且不检查缓冲区大小,容易导致缓冲区溢出。更安全的做法是使用 fgets()

🔧 输入格式匹配与返回值

scanf 严格按照格式字符串进行匹配,如果输入与预期不符,会提前结束并返回已成功读取的数量。常用技巧:

#include <stdio.h>

int main() {
    int a, b;
    printf("请输入两个整数: ");
    int count = scanf("%d %d", &a, &b);
    if (count == 2) {
        printf("a = %d, b = %d\n", a, b);
    } else {
        printf("输入无效!\n");
    }
    return 0;
}

🔤 字符输入输出:getchar / putchar

getchar() 从标准输入读取一个字符(包括空格和换行),putchar() 输出一个字符。

#include <stdio.h>

int main() {
    char ch;
    printf("请输入一个字符: ");
    ch = getchar();          // 读取一个字符
    printf("你输入的字符是: ");
    putchar(ch);             // 输出该字符
    putchar('\n');           // 换行

    // 循环读取直到回车
    printf("请输入一行字符: ");
    while ((ch = getchar()) != '\n') {
        putchar(ch);
    }
    putchar('\n');
    return 0;
}
缓冲区问题: getchar()scanf 都受输入缓冲区影响,例如在 scanf 后直接使用 getchar() 可能会读取到残留的换行符,需要额外处理。

📝 字符串输入输出:fgets / puts

由于 scanf("%s") 不能读取带空格的字符串且不安全,推荐使用 fgets() 读取字符串。输出可使用 puts()printf()

#include <stdio.h>

int main() {
    char line[100];
    printf("请输入一行文字: ");
    fgets(line, sizeof(line), stdin);   // 读取最多99个字符+'\0',包括换行符
    printf("你输入的是: ");
    puts(line);                         // 输出字符串并自动换行

    // 移除fgets读取的换行符(如果需要)
    size_t len = strlen(line);
    if (len > 0 && line[len-1] == '\n') {
        line[len-1] = '\0';
    }
    printf("去掉换行后: %s\n", line);
    return 0;
}
推荐: 使用 fgets() 替代 gets()gets() 在C11中已废弃),可以有效防止缓冲区溢出。

🧹 输入缓冲区问题

当混合使用 scanfgetchar 或多次调用 scanf 时,输入缓冲区中残留的换行符可能导致程序行为异常。典型场景:

#include <stdio.h>

int main() {
    int n;
    char ch;
    printf("输入一个整数: ");
    scanf("%d", &n);
    printf("输入一个字符: ");
    ch = getchar();          // 这里直接读取了上次输入后的换行符
    printf("n=%d, ch=%c\n", n, ch);
    return 0;
}

解决方法:清空缓冲区。常见方式:

  • 使用 while (getchar() != '\n'); 读取并丢弃剩余字符。
  • scanf 格式字符串中加入空格,如 scanf("%d ", &n);(会跳过空白)。
  • 使用 fflush(stdin) 但此方法非标准(C标准未定义),不推荐。
int n;
char ch;
scanf("%d", &n);
while (getchar() != '\n');   // 清空缓冲区
ch = getchar();              // 现在可以正确读取字符

📋 综合示例:简易学生信息录入

#include <stdio.h>
#include <string.h>

#define MAX_NAME 50

int main() {
    char name[MAX_NAME];
    int age;
    float score;
    char gender;

    printf("===== 学生信息录入 =====\n");

    printf("姓名: ");
    fgets(name, sizeof(name), stdin);
    // 去除换行符
    name[strcspn(name, "\n")] = '\0';

    printf("年龄: ");
    scanf("%d", &age);
    while (getchar() != '\n');  // 清空缓冲区

    printf("性别 (M/F): ");
    scanf("%c", &gender);
    while (getchar() != '\n');

    printf("成绩: ");
    scanf("%f", &score);

    printf("\n===== 学生信息 =====\n");
    printf("姓名: %s\n", name);
    printf("年龄: %d\n", age);
    printf("性别: %c\n", gender);
    printf("成绩: %.1f\n", score);
    return 0;
}

⚠️ 常见错误与注意事项

  • 忘记取地址符scanf("%d", age); 错误,应为 &age
  • 类型不匹配double 使用 %lffloat 使用 %f
  • 缓冲区残留:混合输入时未清空缓冲区导致跳过输入。
  • 字符串输入不安全:使用 gets()scanf("%s") 未限制长度可能导致溢出。
  • 误用 fflush(stdin):虽然在某些编译器有效,但C标准未定义,不可移植。
  • 返回值未检查:忽略 scanf 的返回值可能导致使用未初始化的变量。

输入输出是程序与用户交互的桥梁。熟练运用 printfscanf 以及更安全的 fgets 可以让你编写出健壮的交互程序。 下一章我们将学习流程控制语句(条件判断与循环),使程序能够根据不同的条件执行不同的逻辑。