getchar与scanf混用时为何会冲突?

99ANYc3cd6
预计阅读时长 20 分钟
位置: 首页 C语言 正文

getchar() - 逐个字符读取

getchar() 的功能非常单一:从标准输入(通常是键盘)中读取一个字符

c语言 getchar scanf
(图片来源网络,侵删)

函数原型

int getchar(void);
  • 返回值类型是 int,而不是 char,这是一个非常重要的细节。
    • 成功时,返回读取到的字符(这个字符会被提升为 int 类型)。
    • 如果到达文件末尾(End-of-File, EOF),或者发生读取错误,则返回 EOFEOF 通常是一个宏定义,值为 -1,使用 int 类型可以确保 EOF 这个特殊值能够被正确返回。

使用示例

示例 1:读取单个字符

#include <stdio.h>
int main() {
    int c; // 必须是 int 类型,而不是 char
    printf("请输入一个字符: ");
    c = getchar(); // 等待用户输入一个字符并按回车
    printf("你输入的字符是: ");
    putchar(c); // 使用 putchar 输出字符
    printf("\n");
    return 0;
}

运行过程:

  1. 程序运行,打印 "请输入一个字符: "。
  2. 你在键盘上输入 A,然后按回车。
  3. getchar() 读取到字符 'A'c 的值变为 65'A' 的 ASCII 码)。
  4. 程序打印 "你输入的字符是: A"。

示例 2:读取一行字符(直到遇到换行符 \n

#include <stdio.h>
int main() {
    int c;
    printf("请输入一行文本,按回车结束: \n");
    // 循环读取字符,直到遇到换行符 '\n'
    while ((c = getchar()) != '\n') {
        putchar(c);
    }
    printf("\n程序结束,\n");
    return 0;
}

运行过程:

c语言 getchar scanf
(图片来源网络,侵删)
  1. 程序提示输入。
  2. 你输入 Hello World 并按回车。
  3. getchar() 会一个一个地读取:'H', 'e', 'l', 'l', 'o', , 'W', 'o', 'r', 'l', 'd'
  4. 当读取到回车符时,getchar() 得到的是换行符 '\n',循环条件 c != '\n' 不再满足,循环结束。
  5. 程序打印出你输入的内容,然后打印 "程序结束。"。

getchar() 的特点

  • 功能:只读取单个字符。
  • 输入缓冲区:它会从输入缓冲区中取出一个字符,如果缓冲区为空,它会等待用户输入。
  • 回车符的处理:当你在键盘上按回车时,回车符 '\n' 也会被作为一个字符存入输入缓冲区,getchar() 也能读取到它。
  • 主要用途
    • 逐个字符处理输入(过滤掉空格、统计行数等)。
    • 读取单个字符的响应(y/n 选择)。
    • 清空输入缓冲区(while(getchar() != '\n');)。

scanf() - 格式化读取

scanf() 是一个功能强大的格式化输入函数,它可以根据你指定的格式,从标准输入中读取数据,并转换成对应类型的变量。

函数原型

int scanf(const char *format, ...);
  • 返回值:成功读取并赋值的参数个数,如果读取失败(例如输入类型不匹配),则返回 0,如果遇到文件结尾(EOF),则返回 EOF

常用格式说明符

格式说明符 含义 示例
%d 读取一个十进制整数 int a; scanf("%d", &a);
%f 读取一个 float 类型的浮点数 float f; scanf("%f", &f);
%lf 读取一个 double 类型的浮点数 double d; scanf("%lf", &d);
%c 读取一个字符 char c; scanf("%c", &c);
%s 读取一个字符串(直到遇到空白字符) char str[20]; scanf("%s", str);
%c 注意%c 会读取任何字符,包括空格和制表符,而 %s 遇到空格、制表符、换行符就会停止。

使用示例

示例 1:读取整数和浮点数

#include <stdio.h>
int main() {
    int age;
    double height;
    printf("请输入你的年龄和身高( 25 1.75): ");
    // 返回值 2 表示成功读取了 2 个变量
    int result = scanf("%d %lf", &age, &height);
    if (result == 2) {
        printf("年龄: %d, 身高: %.2f\n", age, height);
    } else {
        printf("输入格式错误!\n");
    }
    return 0;
}

运行过程:

  1. 程序提示输入。
  2. 你输入 30 1.82 并按回车。
  3. scanf 成功读取 30age82heightresult 的值为 2
  4. 程序打印 "年龄: 30, 身高: 1.82"。

示例 2:读取字符和字符串

c语言 getchar scanf
(图片来源网络,侵删)
#include <stdio.h>
#include <string.h> // 用于 strlen
int main() {
    char first_initial;
    char last_name[50];
    printf("请输入你的姓的首字母和姓( S Smith): ");
    // 注意 &first_initial,但 last_name 数组名本身就是地址
    scanf(" %c %s", &first_initial, last_name);
    printf("姓的首字母: %c\n", first_initial);
    printf("姓: %s\n", last_name);
    printf("姓的长度: %zu\n", strlen(last_name));
    return 0;
}

运行过程:

  1. 程序提示输入。
  2. 你输入 J Doe 并按回车。
  3. scanf 读取到 'J' 赋给 first_initial,然后读取到 "Doe" 赋给 last_name,注意,"Doe" 之后的空格或换行符会留在缓冲区中。

scanf() 的特点和常见陷阱

  • 必须传递变量的地址scanf 需要知道要把读到的数据存到哪里去,所以除了 %s(数组名本身就是地址)之外,其他所有变量前都必须加 &(取地址符),这是初学者最容易犯的错误。

    • 错误scanf("%d", num);
    • 正确int num; scanf("%d", &num);
  • 输入缓冲区的“幽灵”:这是 scanf 最令人头疼的问题。

    • 场景:当你用 scanf("%d", &a); 读取一个整数后,你按下了回车,回车符 '\n' 会留在输入缓冲区中,如果下一个 scanfscanf("%c", &c);,它会立刻读取到这个残留的 '\n',而不会等待你输入新字符。
    • 解决方案
      1. %c 前加一个空格scanf(" %c", &c);,格式字符串中的空格会告诉 scanf 跳过所有的空白字符(包括空格、制表符、换行符),直到找到一个非空白字符。
      2. 清空缓冲区:在混合使用 scanfgetchar 时,如果遇到问题,可以用 while(getchar() != '\n'); 来清空缓冲区。
  • 输入与格式不匹配scanf 期望一个数字,但你却输入了字母,scanf 会读取失败,并且错误的输入会留在缓冲区中,导致后续的 scanf 也全部失败,程序可能进入死循环。

    • 示例
      int a;
      printf("请输入一个数字: ");
      if (scanf("%d", &a) != 1) {
          printf("输入的不是有效数字!\n");
          // 必须清空缓冲区,否则下一次循环还会失败
          while(getchar() != '\n'); // 清空缓冲区
      }

getchar() vs scanf() 核心区别总结

特性 getchar() scanf()
功能 只读取一个字符 按指定格式读取多种类型的数据
参数 无需参数 需要格式字符串和变量的地址
返回值 读取到的字符(int类型),或 EOF 成功赋值的变量个数,或 EOF
灵活性 低,功能单一 高,非常灵活强大
输入缓冲区 读取缓冲区中的单个字符,包括空格、换行符 根据格式说明符来读取和跳过字符
主要用途 逐个字符处理、清空缓冲区、读取单字符响应 读取结构化数据(如数字、字符串)
易用性 简单,不易出错 复杂,容易因缓冲区问题和地址问题而出错

何时使用哪个?

  • 使用 getchar() 的情况:

    1. 你需要逐个字符地处理输入流(实现一个简单的行编辑器)。
    2. 你只需要读取一个按键(如 y/n 确认)。
    3. 你需要清空 scanf 或其他输入函数留下的“垃圾”在输入缓冲区中。
  • 使用 scanf() 的情况:

    1. 你需要读取明确类型的数据,如整数、浮点数。
    2. 你需要读取一个单词(以空格分隔的字符串)。
    3. 输入的数据格式是固定的、结构化的。

一个混合使用的经典陷阱示例

#include <stdio.h>
int main() {
    int age;
    char name[50];
    printf("请输入你的年龄: ");
    scanf("%d", &age); // 读取年龄
    printf("请输入你的名字: ");
    scanf("%s", name); // 读取名字
    printf("年龄: %d, 名字: %s\n", age, name);
    return 0;
}

如果用户这样输入:

25
Zhang San

会发生什么?

  1. scanf("%d", &age); 读取了 25
  2. 回车符 '\n' 留在了输入缓冲区。
  3. scanf("%s", name); 开始读取,它遇到 '\n',认为这是一个分隔符,于是它停止读取,并将空字符串 赋给了 name
  4. 程序输出:年龄: 25, 名字: (名字为空)。

如何修复? 在读取名字的 scanf 前加一个空格,或者用 getchar 清空缓冲区。

修复方法 1 (推荐):

printf("请输入你的名字: ");
scanf(" %s", name); // 注意 %c 前的空格

这个空格会消耗掉前面留下的换行符,让 scanf 正确等待用户输入 "Zhang San"。

修复方法 2:

printf("请输入你的名字: ");
while(getchar() != '\n'); // 清空缓冲区
scanf("%s", name);

希望这个详细的解释能帮助你彻底理解 getchar()scanf() 的用法和区别!

-- 展开阅读全文 --
头像
dede自定义模型字段如何添加链接?
« 上一篇 今天
织梦ueditor1.3.6有何新功能或问题?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]