gets函数在C语言中到底有什么用?

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

gets 函数的核心作用

gets 函数的全称是 "get string"(获取字符串),它的唯一作用就是从标准输入流(通常是键盘)中读取一行字符,直到遇到换行符 \n 或者文件结束符 EOF,然后将这些字符(不包括换行符)存入一个字符数组(字符串)中,并在末尾自动添加一个字符串结束符 \0

c语言中gets的作用
(图片来源网络,侵删)

函数原型

#include <stdio.h>
char *gets(char *str);
  • 参数char *str 是一个指向字符数组的指针,这个数组必须有足够的空间来存储用户输入的字符串。
  • 返回值
    • 成功时,返回指向 str 的指针。
    • 如果读取失败(例如遇到文件结束符 EOF),或者 strNULL 指针,则返回 NULL

gets 的工作原理与示例

gets 的工作方式非常简单直接:它会一直等待用户输入,直到用户按下回车键,回车键产生的换行符 \n 会被 gets 丢弃,取而代之的是 \0

示例代码

#include <stdio.h>
int main() {
    char name[50]; // 定义一个字符数组来存储名字
    printf("请输入你的名字: ");
    gets(name); // 从键盘读取一行并存入 name 数组
    printf("你好, %s!\n", name); // 打印出读取到的名字
    return 0;

代码解析

  1. char name[50];:我们定义了一个大小为50的字符数组 name,用于存储用户输入的字符串。
  2. printf("请输入你的名字: ");:在屏幕上显示提示信息。
  3. gets(name);:程序在这里暂停,等待用户输入,假设用户输入 Alice 并按下回车。
    • gets 会将 A, l, i, c, e 这五个字符依次存入 name 数组。
    • 它会丢弃回车符 \n
    • 然后在 name 数组的第6个位置(索引为5)添加一个 \0,表示字符串结束。
  4. printf("你好, %s!\n", name);%s 格式说明符会从 name 数组的第一个字符开始打印,直到遇到 \0 为止,屏幕上会显示 你好, Alice!

gets 的致命缺陷:缓冲区溢出

尽管 gets 使用简单,但它被认为是C语言中最危险、最不安全的函数之一,因此在C11标准中被正式移除,在较新的编译器(如GCC, Clang)中,直接使用 gets 会收到编译警告或错误。

什么是缓冲区溢出?

gets 函数不会检查输入的字符串长度,它只会傻傻地一直往你提供的数组里写入字符,直到遇到换行符或 EOF,如果用户输入的字符数量超过了数组的容量,多出来的字符就会溢出数组,覆盖掉内存中数组后面的数据,这会导致严重的后果。

缓冲区溢出的危害

  1. 程序崩溃:如果覆盖了程序的关键数据或代码,可能导致程序异常终止。
  2. 数据被破坏:如果覆盖了其他重要变量的数据,会导致程序逻辑错误。
  3. 安全漏洞:这是最危险的一点,攻击者可以精心构造一段超长的输入,利用缓冲区溢出覆盖函数的返回地址,从而执行任意代码,完全控制你的程序,这是历史上无数网络攻击的根源。

一个危险的示例

#include <stdio.h>
int main() {
    char password[10]; // 假设密码最大长度为9
    printf("请输入密码: ");
    gets(password); // 危险!没有检查输入长度
    printf("密码已设置,\n");
    return 0;
}

攻击场景

c语言中gets的作用
(图片来源网络,侵删)
  • 程序期望用户输入一个不超过9个字符的密码。
  • 攻击者输入一个超过10个字符的字符串,1234567890123456
  • gets 会把这16个字符全部写入 password 数组。
  • 数组只有10个字节大小,后面的6个字节会溢出,覆盖掉 main 函数栈帧中的其他数据,比如函数的返回地址。
  • main 函数执行完毕试图返回时,它会跳转到被恶意篡改的地址,导致程序执行攻击者预设的恶意代码。

安全的替代方案

由于 gets 极其危险,绝对不要在任何新代码中使用它,你应该使用更安全的替代函数。

fgets - 推荐首选

fgets 是最常用、最安全的替代方案。

  • 作用:从指定的流(如 stdin 标准输入)中读取一行。
  • 关键优势:它可以限制读取的最大字符数,从而有效防止缓冲区溢出。

函数原型:

char *fgets(char *str, int n, FILE *stream);
  • str: 存储字符串的字符数组。
  • n: 最多读取的字符数(包括 \0)。这是一个非常重要的安全边界
  • stream: 要读取的流,stdin 代表标准输入(键盘)。

gets 的区别

c语言中gets的作用
(图片来源网络,侵删)
  • fgets保留换行符 \n(如果读取成功且空间足够)。
  • fgets 需要指定最大读取长度。

示例:

#include <stdio.h>
int main() {
    char name[50];
    printf("请输入你的名字: ");
    fgets(name, sizeof(name), stdin); // 安全!最多读取 49 个字符 + 1 个 '\0'
    // 去掉 fgets 读取到的换行符
    for (int i = 0; name[i] != '\0'; i++) {
        if (name[i] == '\n') {
            name[i] = '\0';
            break;
        }
    }
    printf("你好, %s!\n", name);
    return 0;
}

scanf

scanf 也可以用来读取字符串,但用法和限制需要特别注意。

  • 作用:根据格式字符串从输入中读取数据。
  • 用法scanf("%49s", name);(注意这里的 49)。
  • 关键点
    • scanf 遇到空格、制表符、换行符就会停止读取。
    • scanf 无法读取整行,只能读取一个单词。
    • 同样需要指定最大读取宽度(如 %49s)来防止溢出,因为 name 数组大小是50,要留一个位置给 \0

示例:

#include <stdio.h>
int main() {
    char name[50];
    printf("请输入你的名字(单个单词): ");
    scanf("%49s", name); // 安全,但只能读取一个单词
    printf("你好, %s!\n", name);
    return 0;
}

特性 gets fgets (推荐) scanf
作用 从标准输入读取一行 从指定流读取一行 根据格式从输入读取数据
安全性 极不安全,易导致缓冲区溢出 安全,可限制读取长度 相对安全,但需注意宽度
换行符处理 丢弃 \n 保留 \n 遇到 \n 停止读取
能否读取空格 不能,遇到空格停止
参数 gets(str) fgets(str, n, stream) scanf("%s", str)
现代标准 C11已移除 标准库函数 标准库函数

核心结论

永远不要使用 gets 函数。 它是一个历史遗留的、充满安全漏洞的函数,在任何现代C语言项目中,都应该使用 fgets 来安全地读取一行输入。

-- 展开阅读全文 --
头像
C语言项目案例视频,如何快速掌握实战技巧?
« 上一篇 今天
织梦手机端跳转如何设置?
下一篇 » 今天

相关文章

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

目录[+]