一、gets()函数

在读取字符串时,scanf()和转换说明%s只能读取一个单词。可是在程序中经常要读取一整行输入,而不仅仅是一个单词。许多年前,gets()函数就用于处理这种情况。gets()函数简单易用,它读取整行输入,直至遇到换行符,然后丢弃换行符,储存其余字符,并在这些字符的末尾添加一个空字符使其成为一个 C 字符串。它经常和 puts()函数配对使用,该函数用于显示字符串,并在末尾添加换行符。

       /*程序1.1-- 使用 gets() 和 puts() */
#include <stdio.h>
#define LEN 30
int main(void)
{char words[LEN];
gets(words); // 典型用法
printf("Your string twice:\n");
printf("%s\n", words);
puts(words);
return 0;
}

整行输入(除了换行符)都被储存在 words 中,gets()会丢弃换行符’\n’,但是puts()会在字符串末尾添加‘\n’,puts(words)和printf("%s\n,words")的效果相同。
每次运行这个程序,编译器在输出中插入了一行警告消息。这是怎么回事?问题出在 gets()唯一的参数是 words,它无法检查数组是否装得下输入行。在gets()的参数中,数组名会被转换成该数组首元素的地址,因此,gets()函数只知道数组的开始处,并不知道数组中有多少个元素。
如果输入的字符串过长,会导致缓冲区溢出(buffer overflow),即多余的字符超出了指定的目标空间。如果这些多余的字符只是占用了尚未使用的内存,就不会立即出现问题;如果它们擦写掉程序中的其他数据,会导致程序异常中止;或者还有其他情况(比如程序试图访问未分配的内存),有些人通过系统编程,利用gets()插入和运行一些破坏系统安全的代码。

二、fget()函数(和fputs()函数)

fgets()函数通过第2个参数限制读入的字符数来解决溢出的问题。该函数专门设计用于处理文件输入。
fgets()和gets()的区别如下:
1、fgets()函数的第2个参数指明了读入字符的最大数量。如果该参数的值是n,那么fgets()将读入n-1个字符,或者读到遇到的第一个换行符为止。
2、如果fgets()读到一个换行符,会把它储存在字符串中。这点与gets()不同,gets()会丢弃换行符。
3、fgets()函数的第3 个参数指明要读入的文件。如果读入从键盘输入的数据,则以stdin(标准输入)作为参数,该标识符定义在stdio.h中。

因为 fgets()函数把换行符放在字符串的末尾(假设输入行不溢出),通常要与 fputs()函数(和puts()类似)配对使用,除非该函数不在字符串末尾添加换行符。fputs()函数的第2个参数指明它要写入的文件。如果要显示在计算机显示器上,应使用stdout(标准输出)作为该参数。

/*程序2.1--- 使用 fgets() 和 fputs() */
#include <stdio.h>
#define LEN 10
int main(void)
{char words[LEN];
fgets(words, LEN, stdin);
printf("Your string twice (puts(), then fputs()):\n");
puts(words);
fputs(words, stdout);
puts("Enter another string, please.");
fgets(words, LEN, stdin);
printf("Your string twice (puts(), then fputs()):\n");
puts(words);
fputs(words, stdout);
return 0;
}

fputs()函数返回指向char的指针。如果一切进行顺利,该函数返回的地址与传入的第个参数相同。但是,如果函数读到文件结尾,它将返回一个(null pointer)特殊的指针:空指针,该指针保证不会指向有效的数据,所以可用于标识这种特殊情况。
下面的程序2.2演示了一个简单的循环,读入并显示用户输入的内容,直到fgets()读到文件结尾或空行(即,首字符是换行符)。

/*程序2.2-- 使用 fgets() 和 fputs() */
#include <stdio.h>
#define LEN 10
int main(void)
{char words[LEN];
while (fgets(words, LEN, stdin) != NULL && words[0] != '\n')
fputs(words, stdout);
return 0;
}

下面是该程序的输出示例:

123456789asdfghjklzxc
123456789asdfghjklzxc

有意思,虽然LEN被设置为10,但是该程序似乎在处理过长的输入时完全没问题。程序中的fgets()一次读入 LEN - 1 个字符(该例中为 9 个字符)。所以,一开始它只读入了“123456789”,并储存为123456789\0;接着fputs()打印该字符串,而且并未换行。然后while循环进入下一轮迭代,fgets()继续从剩余的输入中读入数据,即读入“asdfghjkl”并储存为asdfghjkl\0;接着fputs()在刚才打印字符串的这一行接着打印第 2 次读入的字符串。然后while 进入下一轮迭代,fgets()继续读取输入、fputs()打印字符串,这一过程循环进行,直到读入最后的“zxc\n”。fgets()将其储存为zxc\n\0, fputs()打印该字符串,由于字符串中的\n,光标被移至下一行开始处。
程序2.3在程序2.2的基础上添加了一部分测试代码。该程序读取输入行,删除储存在字符串中的换行符,如果没有换行符,则丢弃数组装不下的字符。

/* 程序2.3 -- 使用 fgets() */
#include <stdio.h>
#define LEN 10
int main(void)
{char words[LEN];
int i;
while (fgets(words, LEN, stdin) != NULL && words[0] != '\n')
{i = 0;
while (words[i] != '\n' && words[i] != '\0')
i++;
if (words[i] == '\n')
words[i] = '\0';
else // 如果word[i] == '\0'则执行这部分代码
while (getchar() != '\n')
continue;
puts(words);
}
return 0;
}

循环while (words[i] != ‘\n’ && words[i] != ‘\0’)
i++;
遍历字符串,直至遇到换行符或空字符。如果先遇到换行符,下面的if语句就将其替换成空字符;如果先遇到空字符,else部分便丢弃输入行的剩余字符。下面是该程序的输出示例:

This
This
program seems
program s
unwilling to accept long lines.
unwilling

三、空字符和空指针

程序中出现了空字符和空指针。从概念上看,两者完全不同。空字符(或’\0’)是用于标记C字符串末尾的字符,其对应字符编码是0。由于其他字符的编码不可能是 0,所以不可能是字符串的一部分。空指针(或NULL)有一个值,该值不会与任何数据的有效地址对应。通常,函数使用它返回一个有效地址表示某些特殊情况发生,例如遇到文件结尾或未能按预期执行。空字符是整数类型,而空指针是指针类型。两者有时容易混淆的原因是:它们都可以用数值0来表示。但是,从概念上看,两者是不同类型的0。
另外,空字符是一个字符,占1字节;而空指针是一个地址,通常占4字节。

四、gets_s()函数

gets_s()函数和fgets()类似,用一个参数限制读入的字符数。假设把下面的代码将把一行输入中的前9个字符读入words数组中,假设末尾有换行符:
gets_s(words, LEN);
gets_s()与fgets()的区别如下:
1、gets_s()只从标准输入中读取数据,所以不需要第3个参数。
2、如果gets_s()读到换行符,会丢弃它而不是储存它(和gets()函数一样)。
3、如果gets_s()读到最大字符数都没有读到换行符,会执行以下几步。首先把目标数组中的首字符设置为空字符,读取并丢弃随后的输入直至读到换行符或文件结尾,然后返回空指针。接着,调用依赖实现的“处理函数”(或你选择的其他函数),可能会中止或退出程序。

第2个特性说明,只要输入行未超过最大字符数,gets_s()和gets()几乎一样,完全可以用gets_s()替换gets()。第3个特性说明,要使用这个函数还需要进一步学习。
**

五、总结

**
我们来比较一下 gets()、fgets()和 gets_s()的适用性。如果目标存储区装得下输入行,3 个函数都没问题。但是fgets()会保留输入末尾的换行符作为字符串的一部分,要编写额外的代码将其替换成空字符。
如果输入行太长会怎样?使用gets()不安全,它会擦写现有数据,存在安全隐患。gets_s()函数很安全,但是,如果并不希望程序中止或退出,就要知道如何编写特殊的“处理函数”。另外,如果打算让程序继续运行,gets_s()会丢弃该输入行的其余字符,无论你是否需要。由此可见,当输入太长,超过数组可容纳的字符数时,fgets()函数最容易使用,而且可以选择不同的处理方式。如果要让程序继续使用输入行中超出的字符,可以参考程序2.2中的处理方法。如果想丢弃输入行的超出字符,可以参考程序2.3中的处理方法。
所以,当输入与预期不符时,gets_s()完全没有fgets()函数方便、灵活。也许这也是gets_s()只作为C库的可选扩展的原因之一。鉴于此,fgets()通常是处理类似情况的最佳选择。

C语言--gets()、gets_s()、fget()的比较相关推荐

  1. ACL 2022录用结果出炉:国内多支团队晒“战绩”,清华一实验组18篇入选

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 大数据文摘转载自数据实战派 2月24日,第 60届国际计算语言学协会 ...

  2. 清华大学实验组18篇论文被ACL 2022录用

    近日,ACL 2022录用结果出炉,我组18篇论文被ACL 2022录用,其中主会论文13篇,Findings论文5篇.以下为论文列表及介绍:  一  ACL 2022主会 Packed Levita ...

  3. c语言s_gets函数作用,C语言中gets_s(),gets(),fgets()函数的比较。

    先来讲讲大家最熟悉的gets()函数. 1.gets()函数不安全. 2.C11标准委员会已经将其废除,建议能不用尽量不用. 解释: gets()函数的作用:它读取整行输入,直至遇到换行符,然后丢弃换 ...

  4. C语言的字符串输入函数gets_s()

    C语言的字符串输入函数gets_s() C11新增了gets_s()函数(可选),和fgets()函数类似(上一篇博客有介绍< C语言的字符串输入fgets()函数>),用一个参数限制读入 ...

  5. C语言gets_s, _getws_s函数了解

    gets_s  对应头文件<stdio.h> _getws_s对应头文件<stdio.h> or <wchar.h> 原型: char *gets_s(char * ...

  6. C语言关于指针,gets()和gets_s()函数的理解

    使用const修饰一个指针 int a[5] = {0, 1, 2, 3, 4}; //第一种,指针p1不能修改指向地址上的值. const int * p1 = a; //第二种,指针p2不能更改它 ...

  7. c语言终止 fget stdin 输入流

    在阅读Redis源码,有一段是从配置文件.输入流和选项参数中读取配置内容,在调试输入流这一步,虽然能输入,但是我却不知道怎么停止输入流.尝试过使用ctrl + c ,但会使整个程序都终止. void ...

  8. C语言带空格的字符串输入——gets()函数、scanf()函数缺陷以及改进后的gets_s()函数、scanf_s()函数的简单用法(上)

    **在C语言中,带空格的字符串输入问题相对而言还是比较"坑"的,所以呢,小编就决定要专门谈一谈这个问题,希望对于初学者能够有所帮助. 该篇主要简述了C语言中gets()函数以及sc ...

  9. C语言:随笔11--文件操作

    文件通常是驻留在外部介质(如磁盘等)上的,在使用时才调入内存中来. 文件的分类:按数据的组织形式: (1)ASCII文件(文本文件):每一个字节放一个ASCII代码. (2)二进制文件:把内存中的数据 ...

最新文章

  1. 量子计算生态:市场预期、行业应用与“霸权”争夺
  2. maven netty 配置_springboot2.3手册:5分钟用Netty搭建高性能异步WebSocket服务
  3. [Java网络编程基础]TCP发送和接收数据
  4. Python实操:手把手教你用Matplotlib把数据画出来
  5. UE4的MaterialInstance作用
  6. Android开发者指南(7) —— App Install Location
  7. android快速圣经,Android 中级圣经系列之Activity
  8. Linux基础-磁盘阵列RAID
  9. ssh连接服务器 一段时间不动后 无响应
  10. 画图现代计算机系统,教程方法;Win10新惊喜:经典画图界面完全现代化电脑技巧-琪琪词资源网...
  11. python学习笔记3(字符串)
  12. 【周志华机器学习】强化学习
  13. 数领科技|主流BIM软件及公司介绍
  14. Revit数据处理(三)
  15. 计算1+3+5+...+99 的和
  16. 安装Neo4j图型数据库
  17. VMware虚拟机桥接模式配置,设置虚拟机连接公网
  18. android 本地html传递参数,Android WebView适配html加载本地文件并上传
  19. PPTP协议详解及报文解析
  20. 第一次工业革命(三)——蒸汽船的发展

热门文章

  1. UC九游12月报告: 扫描全年成功案例 精细运营是关键
  2. Harbor传奇(4)- 新世(完结篇)
  3. 新概念二册 Lesson 39 Am I all right?我是否痊愈?(复习直接引语变间接引语)
  4. 【springboot+云计算】B/S医院信息管理系统源码(云HIS)
  5. 教你如何下载视频在MP4,PSP,IPOD,手机上看
  6. android 横向竖排文字,[Android]实现文字竖排
  7. pom文件parent标签报错解决办法(不同的方法)
  8. 10行代码实现目标检测_10个网站在线测试您的代码
  9. IPhone X 移动端 兼容性问题
  10. 虽然我不相信神,但今天确实看到“神”了