C语言中从键盘输入字符串时的一些问题[通俗易懂]

C语言中从键盘输入字符串时的一些问题[通俗易懂]C语言中从键盘输入字符串时的一些问题1.scanf()scanf()在输入字符串时有很大的弊端,例如:1).scanf()在从键盘读入字符时并不会根据所定义的字符数组的大小来控制读入多少个,而是从scanf()中传入的地址开始一直访问下一个元素的内存,碰见空格符或者回车符时才停止读入并存入结束符’\0’,这就有可能造成了一个在C中非常严重的问题,访问非法内存.如果…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

C语言中从键盘输入字符串时的一些问题

1.scanf()

scanf()在输入字符串时有很大的弊端, 例如:
1). scanf()在从键盘读入字符时并不会根据所定义的字符数组的大小来控制读入多少个 , 而是从scanf( ) 中传入的地址开始一直访问下一个元素的内存 , 碰见空格符或者回车符时才停止读入并存入结束符’\0’ , 这就有可能造成了一个在C中非常严重的问题 , 访问非法内存 . 如果所输字符数量大于字符数组的长度 , 当scanf()将把字符存入字符数组的最后一个元素后 , scanf()还会继续往后访问内存 , 将接收的字符继续存入 , 但之后的内存是我们未申请的非法内存 , 会发生未知错误 , 例如以下代码:
2). 第二点也就是第一点中的遇到空格符停止读入并存入结束符’\0’, 也就是说scanf()存不了空格符

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#define N 10
void main() { 
   
	char str1[N];
	printf("请输入字符串str1\n");
	scanf("%s", str1);
	printf("str1为:\n%s\n",str1);
	system("pause");
}

Jetbrains全家桶1年46,售后保障稳定

代码中 , 字符数组最多存十个元素 , 结果却输出了超过十个的字符并发生了错误程序异常终止
在这里插入图片描述

2.gets()

gets(char* str)函数 , gets()函数与scanf()类似 , 但读入时只碰见 ‘\n’才停止读入并自动加上 ‘\0’,也存在和scanf()一样的访问非法内存的可能 .

最新版本的C标准(2011年)最终将该功能从其规范中删除。该函数在C ++中已弃用(截至2011年标准,遵循C99 + TC3)。所以不建议使用gets()函数

3.fgets()

fgets(char* str, int n,stdin )函数在输入字符串时是从标准输入流中读取一个长度为(n – 1)的字符串 , 并存放到字符数组str中
其中n是要求得到的字符个数 , 但实际上只会读入(n – 1)个,在最后加一个’\0’ . 如果在读取完(n – 1)个字符之前碰见换行符’\n’ ,读入即结束, 但’\n’ 也会作为有效字符存入字符串中,然后在’\n’后再存入’\0’
1). fgets( )函数的优点是不会再像scanf()函数一样发生访问非法内存的问题 , 也可以存如scanf()存不了的空格符
2). 说起缺点 , fgets在取完(n – 1)个字符之前碰见换行符’\n’ ,’\n’ 也会作为有效字符存入字符串中,然后在’\n’后再存入’\0’
在很多时候我们都不希望有这个换行符的出现 , 只能再写代码消去这个’\n’
3). fgets()函数在连续输入多个字符串时(也不能算是fgets()函数的缺点) , 但有时也会给我们带来麻烦) , 例如:
char str1[10];
char str2[10];
在输入str1时输入了超过9个的字符如 : abcdefghigklm\n
这时我们会发现 , str2根本不需要我们重新在键盘输入 , 而是存入了 abcdefghijklm’\n’ 中的 jklm\n
此时
字符串str1是”abcdefghi”
字符串str2是”jklm”
这是因为输入str1的函数在读取标准输入流中的字符时读到 i 时读取完成 , 此时剩下的jklm\n还在缓存区静静地等待被读取
此时fgets()在输入str2时直接读取了缓冲区的 jklm\n由于遇到了’\n’(或者读入了(n – 1)个字符)再存入’\0’,此时str2的输入已经完成 , 并不需要我们再次输入 .
让我们举个例子来看一下这几个特点 :

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#define N 10
#include<string.h>
void main() { 
   
	char str[N];
	char str1[N];
	char str2[N];
	printf("请输入字符串str\n");
	fgets(str, N, stdin);
	printf("请输入字符串str1\n");
	fgets(str1, N, stdin);
	printf("请输入字符串str2\n");
	fgets(str2, N, stdin);
	printf("str为:\n%s\n", str);
	printf("str1为:\n%s\n", str1);
	printf("str2为:\n%s\n", str2);
	system("pause");
}

运行时并未像scanf()那样出现越界问题导致程序异常终止的情况 , 但也输出了我们不想看到的 ‘\n’ , str2也并不是从我们再次输入的 , 而是从缓存区直接读取的
在这里插入图片描述

4. 我们也可以利用输入单个字符的函数与循环来输入一个字符串

举个例子 , 如以下代码 :

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#define N 10
void main() { 
   
	char str1[N];
	printf("请输入字符串str1\n");
	for (int i = 0, ch;; ++i) { 
   
		ch = getchar();
		if (i == N - 1 || ch == '\n') { 
   
			str1[i] = '\0';
			break;
		}
		str1[i] = ch;
	}
	printf("str1为:\n%s\n", str1);
	system("pause");
}

可以看出输入也可以不越界
在这里插入图片描述
而且也不会像fgets()函数一样输入的字符串小于数组长度(n – 1)时有换行符 ‘\n’ 的问题
在这里插入图片描述
当然 , 循环形式和输入字符的函数还有其他, 例如, 用scanf(“%c”,ch) 也可以 , 这里就不一 一 举例 .

总之 , 对库函数不满意的话 , 自己也可以写一个符合自己要求的函数或代码来实现 .

例如 :

#include<stdio.h>
#include<stdlib.h>
#define N 10
void fun(char* str) { 
   
	for (int i = 0, ch = '\0'; ch != '\n'; ++i) { 
   
		ch = getchar();
		if (i >= N) { 
   //当超出字符串长度时, 继续接受键盘输入的字符,直到输入\n为止
			//这是为了防止字符串输入完成后输入的字符会被下一个需要输入的数据接收
			continue;
		}
		if (ch == '\n' || i == N - 1) { 
   
			str[i] = '\0';
			continue;
		}
		str[i] = ch;
	}
}
void main() { 
   
	char str[N];
	char str1[N];
	char str2[N];
	printf("请输入字符串str\n");
	fun(str);
	printf("请输入字符串str1\n");
	fun(str1);
	printf("请输入字符串str2\n");
	fun(str2);
	printf("str为:\n%s\n", str);
	printf("str1为:\n%s\n", str1);
	printf("str2为:\n%s\n", str2);
	system("pause");
}

如图, 即可以输入空格, 也不会越界 , 也没有从缓存区读取字符存入字符数组的问题
这里是引用

小结 :

在C语言中输入一个字符串时应该根据实际情况 , 灵活运用输入方法 , 并不是有缺点就不能用 .
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/234561.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Java XML XXE 底层防御

    Java XML XXE 底层防御JavaXML相关API是由JAXP定义了相关的XML的通用接口,常见包括DOM/SAX/STAX/XPATH的标准APIJavax.xml.parsers ->DOM/SAXJavax.xml.stream ->STAXJavax.xml.xpath  ->XPATHJavax.xml.bind  ->JAXBjavax.xml.ws

    2022年7月20日
    19
  • 使用VMware安装centos7并配置网络

    使用VMware安装centos7并配置网络准备工作:1.VM虚拟机(本文使用VMware14.1.2)2.Centos7安装包(https://www.centos.org/download/)centos官网提供多个版本下载,一般使用标准的DVD版安装版,大约4.2G。也可以使用mini版,后续自己安装第三方软件。各版本区别:https://wiki.centos.org/Manuals/ReleaseNotes/Cent…

    2022年6月6日
    33
  • 使用JavaScript刷新验证码

    使用JavaScript刷新验证码今天学习了验证码的开发,日常生活中经常点验证码,今天自己也来做一个验证码                           首先是用一个文件产生随机验证码:  &lt;%@pageimport="java.awt.*"%&gt;&lt;%@pageimport="java.util.*"%&gt;&lt;%@pa…

    2025年8月8日
    4
  • 【AC大牛陈鸿的ACM总结贴】【ID AekdyCoin】人家当初也一样是菜鸟

    【AC大牛陈鸿的ACM总结贴】【ID AekdyCoin】人家当初也一样是菜鸟 acm总结帖_ByAekdyCoin     各路大牛都在中国大陆的5个赛区结束以后纷纷发出了退役帖,总结帖,或功德圆满,或死不瞑目,而这也许又会造就明年的各种“炸尸”风波。为了考虑在发退役贴以后明年我也成为“僵尸”的可能性,于是改名曰“总结贴”,不提比赛细节,不提比赛流水账,权当是大学本科生涯中acm生活的点滴记录……   (1)入门篇甲…

    2022年7月23日
    13
  • 7、多协议配置以及dubbo协议详解

    7、多协议配置以及dubbo协议详解提供者指定协议:<dubbo:protocolname=”dubbo”port=”20880″/><dubbo:protocolname=”rmi”port=”1099″/><!–使用dubbo协议暴露服务–><dubbo:serviceinterface=”com.alibaba.hello.api.HelloService”version=”1.0.0″ref=”helloService”protoc

    2022年6月19日
    42
  • 软件项目工作流程图[通俗易懂]

    软件项目工作流程图

    2022年1月29日
    55

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号