scanf()与gets()的冲突

本文详细解析了C/C++编程中scanf()和gets()函数的区别与混合使用可能导致的问题。重点指出,二者在处理输入数据时使用的结束标记不同,这可能导致数据读取错误。提供了两种处理方法:使用while循环清除缓冲区残留的换行符或直接读取残留的换行符,确保后续输入操作的准确性。

scanf()是C/C++的标准输入函数,其作用是从输入流(缓冲区)中读入数据到指定的地址中。以空格、回车、制表符为输入字符串的结束标志,不会将其读入目标地址中。


gets()是在stdio.h中的输入函数,也是从标准输入流中读取一行字符到指定的字符串中,它只有在遇到EOF或回车换行符时才会结束,也不将其读入,而是在末尾添加一个NULL作为结束标记。所以gets()常用来读入一整行字符,而无须担忧其中的空格是否能读入的问题。

二者混合使用时,常会出错。关键就在于二者使用的结束标记不同。输入字符串时,scanf()或cin>>遇到空格、回车、Tab结束,但在缓冲区中还留着这些结束符,此后如果使用gets()想去获取下一行字符串,它碰到的却是前面遗留下来的回车(或者回车之前还有空格等空白符),那么这次gets()就直接失效了。所以就出现了常碰到的第一个字符串变成空白字符串的现象。

处理方法:
方法1,用一句 while(getchar()!='\n');  来处理掉缓冲区里的回车换行符,然后再gets()就正常了。
方法2,如果很明确地就知道缓冲区还留着这么个回车换行符,那就直接读入吧: scanf("\n"); 或者getchar()读入一个字符,然后 接着gets()也就不会有问题了。
转自:http://hi.baidu.com/sky_space/item/54864a1b9fcd0d9e99ce33ce
**不推荐使用 `gets` 函数!** 在 C 语言中,**`gets()` 是一个已被弃用且极其危险的函数**,从 C11 标准开始已经被正式移除。下面我会解释为什么不能用 `gets`,并给出更安全的替代方案。 --- ### ❌ 为什么不能使用 `gets()`? ```c // 危险示例:不要这样写! char buffer[20]; gets(buffer); // 用户可以输入任意长的内容 ``` - `gets()` **不会检查缓冲区大小**,如果用户输入超过数组长度(如超过 XH 或 XM),就会导致 **缓冲区溢出(Buffer Overflow)**。 - 这是严重的安全漏洞,可能导致程序崩溃、数据被破坏,甚至被攻击者利用执行恶意代码。 - 所有现代编译器(如 GCC、Clang、MSVC)在遇到 `gets()` 时都会发出警告或直接报错。 > ⚠️ 警告示例: > ``` > warning: this program uses gets(), which is unsafe. > ``` --- ### ✅ 推荐替代方案:使用 `fgets()` 我们可以用 `fgets()` 安全地读取字符串,包括姓名学号等字段。 #### 修改后的 `Input` 函数(使用 `fgets` 安全输入) ```c void Input(struct student *stu) { char buffer[100]; // 临时缓冲区,足够大 for (int i = 0; i < N; i++) { printf("请输入第 %d 个学生的学号(最多%d位): ", i + 1, XH - 1); fgets(stu[i].num, XH, stdin); // 最多读 XH-1 字符 + 自动加 '\0' stu[i].num[strcspn(stu[i].num, "\n")] = '\0'; // 去掉末尾换行符 printf("请输入第 %d 个学生的姓名(最多%d个字符): ", i + 1, XM - 1); fgets(stu[i].name, XM, stdin); stu[i].name[strcspn(stu[i].name, "\n")] = '\0'; printf("请输入第 %d 个学生的性别(M/F): ", i + 1); while (getchar() != '\n'); // 清空输入缓冲区 scanf("%c", &stu[i].sex); printf("请输入第 %d 个学生的年龄: ", i + 1); scanf("%d", &stu[i].age); printf("请输入第 %d 个学生的成绩: ", i + 1); scanf("%lf", &stu[i].score); getchar(); // 吃掉最后的换行符 printf("\n"); } } ``` --- ### 🔍 关键点解释: #### 1. `fgets(stu[i].num, XH, stdin)` - 从标准输入读取最多 `XH - 1` 个字符(留一个位置给 `\0`),防止溢出。 - 会自动在末尾添加字符串结束符 `\0`。 #### 2. `strcspn(stu[i].num, "\n")` - 查找字符串中是否包含换行符 `\n`(由回车产生) - 将其替换为 `\0`,实现“去掉换行” 例如: ```c // 如果用户输入 "12345\n" // fgets 后得到 "12345\n\0" // strcspn 返回 5,然后 stu[i].num[5] = '\0' → 变成 "12345\0" ``` #### 3. 性别数值输入仍可用 `scanf` 但要注意清理缓冲区,避免 `fgets` 误读残留的换行。 --- ### 🛠️ 更完善的版本(全部使用 fgets 处理) 为了彻底避免混合输入问题(如 `scanf` `fgets` 冲突),建议统一使用 `fgets` 读取所有输入,并配合 `sscanf` 解析。 ```c void Input(struct student *stu) { char buffer[100]; for (int i = 0; i < N; i++) { printf("请输入第 %d 个学生的学号(最多%d位): ", i + 1, XH - 1); fgets(buffer, sizeof(buffer), stdin); sscanf(buffer, "%s", stu[i].num); printf("请输入第 %d 个学生的姓名(最多%d个字符): ", i + 1, XM - 1); fgets(buffer, sizeof(buffer), stdin); sscanf(buffer, "%s", stu[i].name); printf("请输入第 %d 个学生的性别(M/F): ", i + 1); fgets(buffer, sizeof(buffer), stdin); sscanf(buffer, "%c", &stu[i].sex); printf("请输入第 %d 个学生的年龄: ", i + 1); fgets(buffer, sizeof(buffer), stdin); sscanf(buffer, "%d", &stu[i].age); printf("请输入第 %d 个学生的成绩: ", i + 1); fgets(buffer, sizeof(buffer), stdin); sscanf(buffer, "%lf", &stu[i].score); printf("\n"); } } ``` ✅ 优点: - 统一输入方式,避免 `scanf` 缓冲区残留问题 - 更安全、更稳定 --- ### ✅ 总结 | 方法 | 是否推荐 | 原因 | |------------|----------|------| | `gets()` | ❌ 绝对不要用 | 导致缓冲区溢出,已废弃 | | `scanf("%s")` | ⚠️ 慎用 | 不支持空格,可能越界 | | `fgets()` + `strcspn` | ✅ 强烈推荐 | 安全、可控、防溢出 | ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值