**不推荐使用 `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` | ✅ 强烈推荐 | 安全、可控、防溢出 |
---