C语言字符串输入函数 scanf,gets,fgets 的比较与解析

C语言二维字符数组安全输入:scanf/fgets/gets深度解析 二维字符数组是C语言中存储多字符串的基础结构,其本质为连续一维内存,输入操作需严格匹配缓冲区管理、边界控制与换行符处理。scanf因空白符截断和缓冲区残留易致逻辑错乱;gets因无界读取已被C11标准废弃,存在严重缓冲区溢出风险;而fgets通过显式长度参数(n)实现内存安全,成为唯一推荐的字符串输入方案。本文结合内存布局原理与工程实践,详解如何用fgets安全读取含空格中文姓名,并提供可复用的safe_gets封装模板,覆盖OJ提交、实训作业及工业级代码场景。 阅读详情

想先讲一下,关于输入字符串输入

如果是单个字符输入 要在输入前后都要加上 getchar() 读取空字符

个人经验,如有错误请指出。


原博地址:https://blog.youkuaiyun.com/jzqt_t/article/details/38315675

许多ACMer在做ACM题时经常会被一些字符串输入搞混,如果认为几个字符串输入函数的功能基本一样那就大错特错了。今天把C语言中的三个字符串输入函数(scanf,gets,fgets)深入分析一下,以至于不要让太多ACMer字符串输入的阴沟里翻船。(只讨论在命令行中的标准输入)


       首先讨论一下最常见的scanf输入字符串。众所周知,使用scanf("%s")输入字符串有一个显著特点就是字符串中不能有空格,回车,以及制表符等空白字符。因为scanf遇到空白字符就结束字符串输入。下面请看这段代码:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define N 20  
  4.   
  5. int main()  
  6. {  
  7.     char str[N];  
  8.     scanf("%s", str);  
  9.     printf("%s\n", str);  
  10.     return 0;  
  11. }  
编译运行后当你输入“abc def”后会怎么样?程序会得出怎样的结果?下面是我的测试结果

这说明了什么?scanf确实无法读取带有空白字符的字符串。因此在需要读取带空白字符的字符串时使用scanf是一种不明智的选择。


       接下来我讲一个scanf函数一个十分重要的特性:在命令行中用scanf函数做标准输入,只要你是以换行符(也就是回车键)结束输入并且你不是用scanf读取单个字符,那么这个换行符就会被放到输入缓冲区中。如果你的下次输入是输入字符时或者使用gets等函数输入字符串时,你的下次输入就可能会被影响。接下来做个试验,请看这段代码:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define N 20  
  4.   
  5. int main()  
  6. {  
  7.     char str[N];  
  8.     char ch;  
  9.     scanf("%s", str);  
  10.     printf("%s\n", str);  
  11.     ch = getchar();  
  12.     printf("%d\n", ch);  
  13.     return 0;  
  14. }  
编译运行之后我输入“abc”再按回车结束输入,下面是我的测试结果

我用字符变量ch接受后续字符输入再打印该字符的ASCII码值,该字符ASCII码值为10,正好是换行符的ASCII码。光是scanf输入字符串还不够,我们应该试试使用scanf函数输入别的比如整数浮点数之类的来试试,这个试验我就不再在这里一一写代码。我试验过,结果还是一样:输出ch的ASCII码值还是10。说明scanf函数做输入以换行符结束会留下这个换行符在输入缓冲区而不是把这个换行符清除掉。当然我想说在这一种情况下scanf后面就没有换行符了,请看这段代码

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define N 20  
  4.   
  5. int main()  
  6. {  
  7.     char str[N];  
  8.     char ch;  
  9.     scanf("%s", str);  
  10.     printf("%s\n", str);  
  11.     scanf("%c", &ch);  
  12.     printf("%d\n", ch);  
  13.     ch = getchar();     //读取第二个scanf后面的残留字符,实际上没有  
  14.     printf("%d\n", ch);  
  15.     return 0;  
  16. }  
运行后使用和上面一样的输入,结果还是和上面一样的输出。

但是不同的是,程序结束,而是再等待输入,因为第一个scanf输入后留下的换行符被第二个scanf读取了,而换行符之后并没有后续输入,因此getchar函数在等待输入中。说明:并不是scanf生成换行符放在输入缓冲区,而是用户输入的换行符没有被scanf吃掉因此残留在缓冲区而已。


       接下来gets函数了。当我们需要读取带有空白字符的字符串时基本上非gets函数莫属了,gets函数恰好是专门读取有空白字符的字符串的。实际上gets函数一直读到换行符才停止。试验代码如下:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define N 20  
  4.   
  5. int main()  
  6. {  
  7.     char str[N];  
  8.     char ch;  
  9.     gets(str);  
  10.     printf("%s\n", str);  
  11.     printf("%c\n", str[strlen(str)-1]);     //输出字符串str最后一个字符,非'\0'  
  12.     ch = getchar();  
  13.     printf("%d\n", ch);  
  14.     return 0;  
  15. }  
当我输入一个字符串“I am a student”后按回车键结束输入得到的结果是这样的


等待输入的是ch字符,说明gets函数输入字符串并没有像scanf函数一样留下一个换行符在输入缓冲区中。那么难道这个换行符被读到字符串中了?不!因为输出字符串最后一个字符发现这个字符并不是换行符而是字母‘t',这正好是我输入字符串的最后一个字符。说明gets函数会把换行符吃掉但是却并没有放在字符串中,相当于一个会读非换行符的空白字符的scanf函数加一个getchar的功能。


       下面,我们来看一下scanf和gets交替使用来读取字符串的现象。试验代码如下:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define N 20  
  4.   
  5. int main()  
  6. {  
  7.     char str1[N];  
  8.     char str2[N];  
  9.     char ch;  
  10.     scanf("%s", str1);  
  11.     gets(str2);  
  12.     printf("str1=%s;\n", str1);  
  13.     printf("str2=%s;\n", str2);  
  14.     ch = getchar();  
  15.     printf("ch = %d\n", ch);  
  16.     return 0;  
  17. }  
我打算先输入“student”后按下回车键再输入另一个字符串的,可是当我输入完“student”并按下回车后第二个字符串就已经输入完毕了,现象如下:

很明显等待输入的是getchar函数并且字符串str2是一个空串(也就是直接就是字符串结束符'\0'的字符串,长度为0)。我们不妨根据之前得到的结论解释一下这是为什么:scanf函数留下了换行符在输入缓冲区中并且被gets函数读入,因为gets函数遇到换行符才结束字符串的输入,而换行符之前的字符已经被scanf函数读走了,因此gets函数读到的就是一个空字符串了,并且gets函数会吃掉换行符且不会把它放进字符串中,因此目前的getchar函数还在等待输入的状态。事实上你不妨做个试验使用gets函数输入字符串的时候你只按一个回车键,那么你gets函数读到的就是空字符串。很明显的一个结论就是:scanf函数是不可能读到空字符串的,因为它不会读取空白字符,因此会不停的等待输入直到有非空白字符为止。


       下面我们讲第三个字符串输入函数fgets了,fgets本来是文件的输入函数,但是也可以用于标准输入,它比gets函数的优势就是它能限制字符串输入的长度,避免了缓冲区溢出,有许多网络攻击都属于缓冲区溢出攻击。换而言之就是说fgets函数比gets函数安全。我们依旧使用前面的试验代码,只是将gets换成fgets,代码如下:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define N 20  
  4.   
  5. int main()  
  6. {  
  7.     char str1[N];  
  8.     char str2[N];  
  9.     char ch;  
  10.     scanf("%s", str1);  
  11.     fgets(str2, sizeof(str2), stdin);  
  12.     printf("str1=%s;\n", str1);  
  13.     printf("str2=%s;\n", str2);  
  14.     ch = getchar();  
  15.     printf("ch = %d\n", ch);  
  16.     return 0;  
  17. }  
还是一模一样的输入,当输入“student”按回车后fgets就已经完成了输入,这说明fgets和gets函数还是很像的,但是有一个地方不一样,看看我的结果:


很明显等待输入的还是getchar函数,但是字符串str2却和之前不一样了,应该说fgets函数把换行符加到字符串里面去了,str2并不是一个空字符串,而是一个只有一个换行符的字符串,如果你输出str2的长度你就会发现它的长度是1。fgets与gets在输入字符串中除了限制输入字符串的长度以外的主要区别就在于fgets会将换行符读取到字符串里面而不是像gets函数那样将换行符销毁。

C语言安全输入:为什么必须用fgets()替代gets() 缓冲区溢出是C语言最经典的安全漏洞类型,其根源常在于未约束长度的输入函数。`gets()`因完全忽略缓冲区边界而被C11标准彻底移除,成为栈溢出攻击的教科书案例;`fgets()`则通过强制指定缓冲区总容量(而非仅字符数)实现内存安全,其`size`参数本质是防御性契约——确保最多读取`size-1`字节并预留``空间。这种‘显式长度约束+可控截断’的设计,构成了嵌入式、系统编程及安全加固中输入处理的基石。本文深入解析`fgets()`参数语义、换行符处理陷阱与跨平台安全封装,覆盖从计算机二级考试到工业级 阅读详情

相关推荐

C语言安全基石:为什么必须用fgets替代gets

缓冲区溢出是C语言最经典也最危险的安全漏洞类型,其根源常在于不安全的输入函数设计。gets函数因完全缺失长度校验机制,导致栈溢出风险不可控,早在C11标准中即被标记废弃、C17中彻底移除;而fgets通过显式size参数实现边界防护,成为ISO标准推荐的健壮替代方案。它不仅保障内存安全,还支持文件流、管道、套接字等多场景输入,兼顾性能与可移植性。在嵌入式开发、系统编程及教学实践中,掌握fgets的换行符处理、截断检测与错误恢复机制,是构建高可靠性C程序的关键能力。

weixin_30423977的博客 905

C语言字符串输入函数的比较与解析

在做ACM题时经常会被一些字符串输入搞得

JZQT_T的博客 5099

【OpenVINO】OpenVINO C# API 常用 API 详解与演示

在本文中我们基于模型推理流程,演示了OpenVINO C# API使用方法,并和OpenVINO C++API进行了对比,展示了OpenVINO C# API与C++API在使用的区别,这也对使用过C++ API的开发者十分友好,上手会十分容易。在本文中我们只展示了基础的模型推理流程代码,也对各个API进行了测试,针对其他比较高级的API方法,我们后续会继续进行测试其他API方法,向各位开发者展示其用法。

grape_yan的博客 1539

c语言——输入方式gets,scanf,fgets分析

我们使用最广泛的输入方式应该是scanf函数,scanf可输入的数据类型范围广,使用便捷。但其也不乏缺陷,而且某些时候使用gets和getchar会更加高效。

x18037177724的博客 3452

如何在高版本的ubuntu系统里安装gcc4.1.2

1、下载安装包,一共五个http://archive.ubuntu.com/ubuntu/pool/universe/g/gcc-4.1/,根据计算机系统位数   进行下载,64位下载amd64结尾的。开头分别是cpp-4.1_4.1.2、g++-4.1_4.1.2、gcc-4.1-base_4.1.2、gcc-     4.1_4.1.2、libstdc++6-4.1 2、使用sudo d

lsr_fighting的博客 1931

FPGA人脸识别检测(先获取人脸图像以及灰度二值化)

由于图片有空洞等缺陷,所以后面要进行中值滤波、腐蚀、膨胀等算法处理,然后再对人脸进行框选,最后显示出来。最终的成像应该是下面这样的。(后面有时间再接着做哈哈哈)5、通过modelsim将操作后的图片数据写入gray_img.txt里面后,用matlab将txt转成图片。1、先用matlab将图片转换成txt数据,代码如下。读取图片的方式按照博客咸鱼里的方式来做,如下。2、FPGA读取txt数据。4、FPGA顶层模块及仿真。3、FPGA灰度二值化。

weixin_46312715的博客 2531

C语言——字符串函数(七千字详解)

自从学会了字符串函数,做题有速度了,头发也长起来了。

2202_75625589的博客 7196

S7-1200 PN与G120变频器控制起停及调速PROFINET实现详解

S7-1200 PLC与G120变频器通过PROFINET通信可实现高效、实时的电机控制。2. 《S7-1200与G120 CU250S-2PN的PROFINET通讯指南》- 控制器:S7-1200 PLC (如1214C DC/DC/DC)- 变频器:G120 CU250S-2PN (带PROFINET接口)- 输出区(PZD1-2):控制字(16位)、速度设定值(16位)- 输入区(PZD1-2):状态字(16位)、实际速度值(16位)- P922=1:选择报文类型为"标准报文1 (PZD2/2)"

电工 1866

C语言——字符串函数(必收藏)

超细超用心的字符串函数详解(必收藏噢!入股不亏!)

weixin_59174190的博客 1万+

基于Simulink的转向控制系统仿真建模示例

本文完成了基于 Simulink 的 EPS 转向控制系统仿真,实现了:✅掌握 EPS 助力控制基本原理✅构建车速敏感的可变助力策略✅在 Simulink 中验证低速轻便、高速沉稳的手感✅达成“安全、舒适、高效”的现代转向系统。

xiaoheshang_123的博客 86

基于MATLAB的数字滤波器之低通滤波器

本文首先介绍了数字低通滤波器的概念和原理,其次论述了低通滤波器的技术指标和设计步骤,最后给出了数字低通滤波器应用于信号去噪的MATLAB代码。

总结论文中的算法并给出论文的复现代码 3万+

C语言的字符串常用函数(18种)

1. gets() (现在已经不怎么用了) (1) 头文件 #include <stdio.h> (2) 函数原型 char *gets(char *s); (3) 功能 从标准输入设备读入字符,并保存到指定的内存空间, 直到出现换行符或读到文件结尾为止 (4) 参数 s: 字符串首地址 (5) 返回值 成功: 返回读入的字符串 失败: NULL (6) 和scanf("%s",s);区别 scanf(

m0_63690488的博客 1万+

基于STM32F103的智能门禁门锁门卫系统设计

本文设计了一种基于STM32F103单片机的智能门禁系统,集成了密码、射频IC卡和指纹识别三种验证方式。系统采用模块化设计,包含MFRC522射频模块、AS608指纹模块、矩阵键盘、OLED显示和舵机控制等硬件,实现了身份验证、密码修改、IC卡/指纹管理以及安全防护功能。测试结果表明系统响应迅速(密码验证250ms、指纹验证600ms)、功耗低(待机50mA),且具备掉电密码保存功能。该系统操作简便、功能完善,适用于家庭和办公场所,具有较高实用价值。未来可扩展远程控制、人脸识别等功能以进一步提升智能化水平。

3254968421的博客 318

C语言字符串安全输入输出:从缓冲区溢出到防御性编程实战

在C语言编程中,字符串处理是基础且关键的技术环节,其核心原理涉及字符数组的内存布局与空字符终止规则。不安全的输入操作,如使用未限制宽度的scanf或已被废弃的gets函数,极易引发缓冲区溢出这一经典安全漏洞,轻则导致程序崩溃,重则被恶意利用实施攻击。从技术价值看,掌握安全的字符串处理方法是编写健壮、可靠C程序的基石,尤其在系统编程、嵌入式开发等对稳定性和安全性要求极高的领域。应用场景广泛,包括命令行工具开发、网络协议解析、文件数据处理等任何需要用户交互或外部数据输入的场合。本文聚焦于C语言字符串输入输出的安

weixin_33690963的博客 474

【回眸】Tessy集成测试软件使用指南(一)新手使用篇

这个专栏的文章前4篇都在记录如何使用Tessy进行单元测试,集成测试需要有一定单元测试基础,且做集成测试之前,需要做好单元测试,否则将会大幅增加软件单元代码纠错的代价。集成测试所花费的时间通常远远超过单元测试。如果直接从单元测试到系统测试,结果往往是不理想的。集成测试可以检测由于单元相互交互而导致的错误,通常由一组专业的测试人员执行。

啤酒鸭的物联网开发记录博客 5278

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

c语言中gets 、getchar 、fgets 、scanf的用法

原文地址:https://www.cnblogs.com/-lyric/p/5118666.html                   http://c.biancheng.net/view/379.html gets——从标准输入接收一串字符,遇到'\n'时结束,但不接收'\n',把 '\n'留存输入缓冲区;把接收的一串字符存储在形式参数指针指向的空间,并在最后自动添加一个'\0'。 g...

dragon_cdut的博客 6204

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

fgets、gets、scanf函数读入字符串比较

首先,说说这个函数的使用方法。代码如下: char str[10]; fgets(str, 10, stdin);//stdin表示从输入流中读入,也可以是其他文件指针。 gets(str); scanf("%s", str);其中fgets函数是安全的,gets和scanf函数都是不安全的。当输入的字符大于等于10时,会数组越界。编译不会保存,甚至运行时也不会保存。但极可能因为对str数组

软件与编程的零零碎碎 1万+

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

C语言中的输入输出艺术:深入解析puts/printf与gets/scanf

在C语言编程中,输入输出操作是构建程序与用户交互的基石。puts()和printf()作为标准输出函数,而gets()(尽管已不推荐使用)和scanf()作为标准输入函数,它们共同构成了C语言程序中不可或缺的一部分。本文将深入探讨这些函数的工作原理、使用场景、性能考量、安全性问题以及在现代编程实践中的最佳实践,同时扩展讨论到fgets()和sscanf()等更安全的替代方案。

星途码客的博客 1994

【C / C++】fgets, gets, getchar,和 scanf 的对比及使用建议

本文对比分析了C/C++中常见的输入函数及其特性。首先指出scanf作为C语言基础输入函数存在格式必须预先确认、VS编译警告、无法读取空格等问题,其设计源于C语言的过程式弱类型特性,而C++的cin通过运算符重载实现了类型自动识别。针对scanf的安全隐患,建议使用"%9s"等格式限定输入长度或改用VS专有的scanf_s函数。对于带空格的输入,介绍了"%[^\n]s"格式和更推荐的fgets函数,后者可限制读取长度并自动处理换行符。文章还提及已被废弃的gets函数的

jebdhs的博客 980

scanf,gets以及fgets输入字符串区别分析

scanf,gets以及fgets差异分析

后山人 1194

fgets ,gets, getchar, scanf基本的使用

fgets ,gets, getchar, scanf函数1.scnaf()2.fgets()3.gets()4.getchar() 1.scnaf() 函数原型 #include <stdio.h> int scanf(const char *format, …); 功能:将从键盘输入的字符转化为“输入控制符”所规定格式的数据,然后存入以输入参数的值为地址的变量中。 scanf进行输...

大概只有风丶 1018

gets 、getchar 、fgets 、scanf的用法

原文地址:                   http://c.biancheng.net/view/379.html          ...

嵌入式与Linux那些事的博客 1541

C语言输入输出函数对比:scanf vs getchar vs gets 的5个关键差异与缓冲区陷阱

本文深入对比了C语言中scanf、getchar和gets三种输入输出函数的关键差异与缓冲区陷阱,重点分析了它们在安全性、缓冲区处理和格式控制方面的不同。通过实际代码示例和解决方案,帮助开发者避免常见漏洞,提升输入处理的健壮性。特别强调了gets函数的安全风险及替代方案,是C语言开发者必读的输入输出函数指南。

435

C语言二维字符数组安全输入:scanf、gets与fgets深度对比

二维字符数组在C语言中本质是连续内存块,其输入操作直接受限于缓冲区边界控制。scanf因缺乏内置长度约束,易引发缓冲区溢出;gets已被C11标准移除,存在不可修复的安全缺陷;而fgets强制要求指定最大读取字节数,天然支持整行读取(含空格)并杜绝越界风险。从内存布局原理出发,fgets通过size参数建立明确的安全契约,成为处理char matrix[rows][cols]类结构的唯一推荐方案,广泛应用于命令行交互、配置解析及嵌入式输入等工程场景。

weixin_33924220的博客 378

C语言gets函数安全漏洞解析:从缓冲区溢出到fgets替代方案

缓冲区溢出是计算机安全领域的经典漏洞类型,其原理在于程序向分配的内存区域写入超出其容量的数据,导致相邻内存被覆盖。这种漏洞可能引发程序崩溃、数据损坏,甚至被恶意利用执行任意代码,构成严重安全威胁。在C语言编程实践中,字符串输入处理是缓冲区溢出的高发场景。gets函数由于缺乏边界检查机制,成为典型的安全隐患源。现代C语言标准已废弃gets,推荐使用fgets等安全替代方案。fgets通过显式指定缓冲区大小参数,从原理上杜绝了缓冲区溢出风险,是构建健壮C程序的基础。本文聚焦C语言输入安全,深入剖析gets函数缺

weixin_33904756的博客 805

C语言:缓冲区及字符串的输入与输出

引言 字符串在大多数编程语言中都有着举足轻重的地位,在C语言中,字符串亦是如此. 本篇博客将简单介绍一下缓存区的概念和几个跟字符串输入输出有关的函数. 一、缓冲区 概念 系统为了加快程序运行的速度预留出来的、暂时置放输出或输入资料的一块内存空间 为什么需要缓冲区 程序运行时,在CPU中控制单元控制下,计算机从磁盘中读取对应的数据。但是与内存相比,磁盘的读写速度是很慢的,如果我们每次获取数据都从磁盘...

NiGo的博客 5310
上一篇: 关于线段树
下一篇: 51Nod - 1073 约瑟夫环
OneLine_
博客等级 码龄9年 48粉丝 72原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值