C语言中gets函数是什么意思?

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

gets在C语言中什么意思?【深度解析】从入门到避坑,一篇讲透!

** 本文将彻底讲清楚C语言中gets()函数的含义、用法、工作原理,并重点揭示其致命的安全隐患,无论你是C语言初学者还是希望巩固基础的开发者,阅读本文后,你不仅能明白gets()是什么,更能知道为什么在现代编程中应该坚决弃用它,并学会使用安全的替代方案。

gets在c语言中什么意思
(图片来源网络,侵删)

开篇直击:用户最关心的问题

gets在c语言中什么意思?”—— 当你通过百度搜索这个问题时,你很可能正处于以下几种情况之一:

  1. C语言初学者: 刚学到标准输入输出(stdio.h),在教材或代码中遇到了gets()这个函数,好奇它的作用。
  2. 遇到编译警告/错误: 在使用gets()时,编译器(如GCC)给出了警告或弃用提示,让你感到困惑。
  3. 寻求安全编码知识: 听闻gets()函数有“漏洞”,想深入了解其风险所在。

别担心,本文将为你一一解答,我们将从最基础的概念讲起,逐步深入到其致命的缺陷和最佳实践。

gets()函数详解:它到底“干”什么的?

getsget string 的缩写,从字面上就能理解它的功能:从标准输入(通常是键盘)读取一行字符,并将其存储到一个字符数组(字符串)中。

函数原型

gets在c语言中什么意思
(图片来源网络,侵删)

要理解一个函数,首先要看它的“身份证”——函数原型。gets()函数在C标准库<stdio.h>中的声明如下:

char *gets(char *str);
  • 参数: char *str,这是一个指向字符数组(即字符串)的指针,你将把用户输入的内容存储在这个数组里。
  • 返回值:
    • 成功时,返回一个指向str的指针(也就是你传入的数组地址)。
    • 读取到文件结束符(EOF)或发生错误时,返回NULL指针。

工作原理:它是如何“读取”的?

gets()函数的工作方式非常“简单粗暴”:

  • 它会开始在标准输入流中逐个读取字符。
  • 它会持续读取,直到遇到一个换行符\n(也就是你按下回车键的那一刻)或者文件结束符(EOF)
  • 关键一步:它会自动丢弃那个换行符\n,并在字符串的末尾自动添加一个空字符\0,以构成一个标准的C语言字符串。
  • 它将这个处理好的字符串返回给你。

代码示例:亲身体验gets()

下面是一个简单的例子,让你直观感受gets()的行为:

#include <stdio.h>
int main() {
    char name[50]; // 定义一个可以容纳49个字符+1个'\0'的数组
    printf("请输入你的名字: ");
    // 使用gets()读取用户输入
    char *result = gets(name);
    if (result != NULL) {
        printf("你好, %s!\n", name);
        printf("函数返回的指针指向: %p\n", result);
        printf("数组name的地址是: %p\n", name);
    } else {
        printf("读取输入失败或遇到EOF,\n");
    }
    return 0;
}

运行逻辑分析:

  1. 程序运行,打印提示信息“请输入你的名字: ”。
  2. 程序暂停,等待你在键盘上输入内容并按下回车。
  3. 假设你输入 Alice 并按下回车。
  4. gets(name) 会读取 A l i c e \n,它发现换行符\n,于是停止读取,丢弃\n,并在e后面加上\0
  5. 字符数组name变成了:{'A', 'l', 'i', 'c', 'e', '\0'}
  6. 程序继续执行,打印出“你好, Alice!”。

致命的陷阱:为什么gets()是“危险”的?

如果你只学到上面这一步,可能会觉得gets()很方便,但作为一名负责任的程序员专家,我必须用最强烈的语气告诉你:gets()是C语言中最危险、最臭名昭著的函数之一,在任何情况下都不应该在生产代码中使用它!

它的致命缺陷在于:它无法检查输入的长度,极易导致缓冲区溢出。

什么是缓冲区溢出? 在我们的例子中,name数组的大小是50字节,它最多只能安全地存储49个字符(第50个留给\0)。

我们来看一个灾难性的场景:

#include <stdio.h>
int main() {
    char name[10]; // 故意定义一个很小的数组
    printf("请输入你的名字(试试输入超过10个字符): ");
    gets(name); // 危险!
    printf("你好, %s!\n", name);
    return 0;
}

会发生什么? 假设你输入了一个超长的名字,ThisIsAVeryLongName

  1. gets()会傻乎乎地继续读取,它根本不知道name数组只有10个字节的空间。
  2. 它会把 T h i s I s A V e r y L o n g N a m e 这些字符一个不漏地往name数组里“硬塞”。
  3. 当它塞满第10个位置后,还会继续往后面的内存地址写入数据,这会覆盖掉name数组之后的内存区域
  4. 被覆盖的内存区域可能存放着其他重要的变量、函数的返回地址,甚至是程序的栈结构,这会导致不可预测的后果:
    • 程序崩溃(Segmentation Fault)。
    • 程序行为异常。
    • 更严重的是,这会成为黑客攻击的入口! 精心构造的输入可以覆盖函数的返回地址,将程序执行流劫持到恶意代码上,这就是著名的“栈溢出攻击”(Stack Overflow Attack)。

因为gets()的这个巨大安全漏洞,它已经在 C11标准中被正式移除,现代的编译器(如GCC, Clang)在检测到使用gets()时,都会给出严厉的警告或错误信息。

安全替代方案:告别gets(),拥抱现代C

既然gets()不能用,那我们如何安全地读取一行字符串呢?答案是使用 fgets() 函数。

fgets() 函数详解

fgets()file get string 的缩写,它是一个更安全、更灵活的函数。

函数原型:

char *fgets(char *str, int n, FILE *stream);

参数解析:

  • char *str: 同gets(),用于存储读取内容的字符数组指针。
  • int n: 这是安全的关键! 指定要读取的最大字符数(包括结尾的\0)。fgets()最多会读取 n-1 个字符,然后自动在末尾添加\0,从而完美地防止缓冲区溢出。
  • FILE *stream: 指定输入流。gets()默认从stdin(标准输入,即键盘)读取,而fgets()可以让你指定从哪里读,比如stdin、一个文件指针等。

fgets()gets() 的关键区别

特性 gets() fgets()
安全性 极低,不安全 高,安全
缓冲区检查 ,极易溢出 ,通过参数n限制
换行符处理 丢弃 \n 保留 \n(如果读取空间足够)
输入流 stdin 可自定义(如stdin, 文件)

使用fgets()重写上面的例子

#include <stdio.h>
int main() {
    char name[50];
    printf("请输入你的名字: ");
    // 使用fgets()安全地读取
    // sizeof(name) 会计算出数组大小50,确保不会溢出
    fgets(name, sizeof(name), stdin);
    // 处理fgets()可能留下的换行符
    // 因为fgets()会保留'\n',我们需要手动去掉它
    for (int i = 0; name[i] != '\0'; i++) {
        if (name[i] == '\n') {
            name[i] = '\0'; // 将换行符替换为字符串结束符
            break;
        }
    }
    printf("你好, %s!\n", name);
    return 0;
}

代码解析:

  • fgets(name, sizeof(name), stdin); 这行代码确保了fgets最多读取49个字符(因为sizeof(name)是50),然后自动在第50个位置加上\0,从根本上杜绝了溢出的可能。
  • 后续的for循环是为了处理fgets()的一个“小特点”:如果输入的字符数少于n-1,它会保留换行符\n,为了得到和gets()一样的干净字符串(不带\n),我们手动查找并替换掉它。

总结与最佳实践

核心要点回顾:

  1. gets()是什么? 它是一个从标准输入读取一行字符串的C库函数。
  2. 它为什么危险? 因为它不检查输入长度,极易导致缓冲区溢出,引发程序崩溃甚至安全漏洞。
  3. 现在还用它吗? 绝对不要! 它已在C11标准中被移除,是过时且危险的函数。
  4. 应该用什么? 使用更安全、更现代的 fgets() 函数。
  5. 如何使用fgets() 记住关键三要素:fgets(缓冲区, 缓冲区大小, 输入流),并记得处理可能存在的换行符。

给C语言学习者的忠告:

在编程的世界里,“能用”不代表“该用”gets()虽然语法简单,但它隐藏的巨大风险就像一颗定时炸弹,作为程序员,我们的首要职责是编写可靠、安全、健壮的代码。

从今天起,请彻底将gets()从你的知识库和代码中“拉黑”,当你需要从控制台读取一行字符串时,请毫不犹豫地选择fgets(),这个小小的习惯,将标志着你从一名新手程序员,向一名具备专业素养和安全意识的开发者的转变。

希望这篇文章能彻底解答你的疑惑,如果你觉得有用,欢迎点赞、收藏并分享给更多需要的朋友!


-- 展开阅读全文 --
头像
织梦网站地图制作教程?如何快速生成?
« 上一篇 今天
dede如何调用栏目名称?
下一篇 » 今天

相关文章

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

目录[+]