C 库函数 – scanf()(一文讲透)

C 库函数 – scanf() 的全面解析:从入门到实战

你是否曾在写 C 语言程序时,面对用户输入束手无策?比如想让用户输入一个数字,但程序总是“读不进去”?别急,这正是 C 库函数 – scanf() 的主场。它就像是 C 语言的“输入翻译官”,负责把用户从键盘敲下的文字,准确无误地转换成程序能理解的变量值。

作为 C 语言中最常用的输入函数之一,scanf() 虽然功能强大,但初学者常因格式控制符使用错误、缓冲区残留等问题踩坑。这篇文章,我将带你从零开始,系统掌握 C 库函数 – scanf() 的核心用法、常见陷阱和实战技巧,让你在面对输入时不再慌张。


什么是 C 库函数 – scanf()?

scanf() 是 C 标准库(<stdio.h>)中的一个函数,全称是 “scan formatted”,意为“按格式扫描输入”。它的主要作用是从标准输入(通常是键盘)读取数据,并根据指定的格式,将其存入变量中。

你可以把它想象成一个“数据捕手”:你告诉它“我要读一个整数”或“我要读一个浮点数”,它就乖乖地从键盘上把对应的数据抓过来,放进你准备好的“容器”(变量)里。

基本语法结构

int scanf(const char *format, ...);
  • format:格式控制字符串,定义了要读取的数据类型和格式。
  • ...:可变参数列表,即要存储数据的变量地址(必须用 & 取地址)。
  • 返回值:成功读取的变量个数,若输入失败则返回 EOF(文件结束符)。

⚠️ 重要提示:所有变量在传入 scanf() 时,必须加上 & 取地址符,否则程序会崩溃或出现不可预期的结果。


常见格式控制符详解

scanf() 的强大之处在于它的格式控制符,这些符号决定了你读取的是什么类型的数据。下面列出最常用的一些:

格式控制符 读取类型 示例输入 说明
%d 十进制整数 123 适用于 int 类型
%f 单精度浮点数 3.14 适用于 float 类型
%lf 双精度浮点数 3.1415926 适用于 double 类型
%c 单个字符 a 读一个字符,包括空格
%s 字符串(以空格或换行结束) hello world 不包含空格,遇到空格停止
%[abc] 字符集合 abc123 只读取 a、b、c 中的字符

说明:%lf%f 的区别在于,%f 用于 float,%lf 用于 double。如果你用 %f 读取 double 类型变量,结果可能错误。


实战案例一:读取基本数据类型

让我们从最简单的例子开始。假设你想让用户输入一个年龄和身高,然后输出。

#include <stdio.h>

int main() {
    int age;
    float height;

    // 提示用户输入
    printf("请输入你的年龄:");
    scanf("%d", &age);  // 读取整数,必须加 &

    printf("请输入你的身高(米):");
    scanf("%f", &height);  // 读取浮点数,必须加 &

    // 输出结果
    printf("你今年 %d 岁,身高 %.2f 米。\n", age, height);

    return 0;
}

代码注释说明:

  • printf("请输入你的年龄:");:输出提示信息,引导用户输入。
  • scanf("%d", &age);%d 表示读一个整数,&age 是变量 age 的地址,告诉 scanf 把读到的数据存到哪里。
  • scanf("%f", &height);%f 读 float 类型,注意不要写成 %lf,因为 height 是 float。
  • %.2f:输出时保留两位小数,提升可读性。

运行结果示例:

请输入你的年龄:25
请输入你的身高(米):1.75
你今年 25 岁,身高 1.75 米。

实战案例二:读取字符串与字符

字符串输入是初学者最容易出错的部分。%s 会自动读取直到遇到空格或换行,因此无法读取带空格的句子。

#include <stdio.h>

int main() {
    char name[50];      // 定义字符数组,最多存 49 个字符 + 1 个 '\0'
    char gender;        // 存储性别字符

    printf("请输入你的名字:");
    scanf("%s", name);  // 注意:name 是数组名,本身就是地址,无需 &

    printf("请输入你的性别(M/F):");
    scanf(" %c", &gender);  // 前面加空格,跳过换行符或空格

    printf("你好,%s!你是 %c 性。\n", name, gender);

    return 0;
}

关键点说明:

  • char name[50];:定义一个最多能存 50 个字符的数组,包括结尾的 \0
  • scanf("%s", name);:数组名本身就是地址,所以不需要 &
  • scanf(" %c", &gender);:前面的空格是“吃掉”前一次输入后残留的换行符。如果没有这个空格,%c 会读到上一个换行符 \n,导致性别输入被跳过。

常见陷阱与解决方案

陷阱 1:缓冲区残留问题

当你连续使用 scanf() 时,输入流中可能残留换行符(\n),导致下一个 scanf() 读到的是换行符,而不是用户输入。

错误示例:

int a, b;
printf("输入 a:");
scanf("%d", &a);
printf("输入 b:");
scanf("%d", &b);  // 如果用户输入 123\n,a 读 123,b 读到 '\n',导致输入失败

解决方案: 使用 getchar() 清除缓冲区,或在格式字符串前加空格。

scanf("%d", &a);
getchar();  // 清除换行符
scanf("%d", &b);

或更优雅地:

scanf("%d", &a);
scanf(" %d", &b);  // 空格会跳过空白字符(空格、制表符、换行)

陷阱 2:字符串溢出风险

使用 %s 时,如果用户输入的字符串过长,会超出数组边界,导致内存溢出,程序崩溃。

危险写法:

char str[10];
scanf("%s", str);  // 用户输入 "hello world",会写入超出 10 字节的内存

安全写法:

char str[10];
scanf("%9s", str);  // 限制最多读 9 个字符,留 1 个给 '\0'

✅ 原则:永远用 [%d]s 的方式限制最大长度,避免缓冲区溢出!


高级用法:一次读取多个变量

scanf() 支持一次读取多个变量,只要格式控制符和变量数量匹配。

#include <stdio.h>

int main() {
    int id;
    float price;
    char product[30];

    printf("请输入商品编号、价格和名称:");
    scanf("%d %f %s", &id, &price, product);

    printf("商品编号:%d,价格:%.2f 元,名称:%s\n", id, price, product);

    return 0;
}

输入示例:

1001 99.99 电脑

输出:

商品编号:1001,价格:99.99 元,名称:电脑

💡 提示:多个变量之间用空格、制表符或换行分隔都可以,scanf 会自动跳过空白字符。


总结:C 库函数 – scanf() 的使用心法

  • 格式控制符要对%d 用于 int,%f 用于 float,%lf 用于 double。
  • 变量必须加 &:除了数组名,所有变量传入 scanf() 都要取地址。
  • 注意缓冲区残留:使用 getchar() 或格式前加空格解决。
  • 防止字符串溢出:使用 %.Ns 限制输入长度。
  • 返回值要检查scanf() 返回成功读取的变量个数,建议判断是否等于预期值。

C 库函数 – scanf() 虽然看似简单,但背后涉及输入流、缓冲区管理、类型匹配等机制。掌握它,不仅是写好输入程序的关键,更是理解 C 语言底层机制的起点。

当你能熟练使用 scanf(),你就离“真正的 C 程序员”又近了一步。别怕出错,多试几次,你会发现,原来输入也可以如此优雅。

现在,打开你的编译器,写下第一个 scanf() 程序吧。