目录索引
输入函数
- 主要从3个板块来介绍3种类型的输入函数
输入函数的原理:一般来说,当程序执行到scanf等输入函数时,会从内存的缓冲区读取数据,如果缓冲区是空的,就阻塞住,等待你从键盘输入。直到出现空白符(空白符指空格符、回车符、制表符,制表符即Tab键盘)才停止。但是也会有像getch这样的特例
板块1
scanf
- scanf的用法
函数原型 :int scanf(const char *format, ...)
函数用法:scanf("格式化字符串",(变量地址) );
用法示例:
int a;
scanf("%d",&a);
- 注意事项:
(1)用法举例中的&为取地址符,因为a只是一个整型变量,所以用&取a的地址之后的 ,&a 才是一个完整的变量地址
(2)字符串和数组不用 & 来取地址,是因为字符串的变量名在 scanf 语句里表示的就是地址
例如:
char b[60];
scanf("%s",b);
中sacnf 中的b前没加&,是因为b是字符串,不需要取地址
参考资料:当我们声明一个数组时,数组元素的储存地址就赋给了数组名,因此数组名就是一个地址。而字符串是特殊的数组,所以字符串和数组就不用 & 再去取地址了。
(3)scanf格式输入时要求输入格式与格式控制符中的完全一样
例如,scanf("abcd%c",&ch); 输入时必须输入abcde和ch得到的值才可以,其中ch表示的是字符变量ch的内容
(4)scanf读取字符串时以空格为分隔
例如,scanf("%d%d",&x,&y); 输入的两个整数之间要空白符隔开(空白符:回车、空格、制表符)
- 函数特点
(1)scanf可用于接收全部类型的变量
(2)scanf() 读取字符串时以空白符为分隔,遇到空白符就认为当前字符串结束了,所以无法读取含有空格的字符串。但是使用"%[^\n]"可以使scanf接收输入带空格的字符串,直到回车才结束接收(%[^ ]意思是输入任意多的字符,直到遇到指定的字符。 ^ 表示取反)例如:char str[10]; scanf("%[^\n]",str); //可以读取空格,回车结束输入
PS:空白符是空格、回车、制表符(Tab键)的总称
(3)scanf忽略先导的空白符。就是不管是有几百个回车也好,几万个空格也罢,只要它们连续地出现在缓冲区的开头,就统统忽略他们,然后再读有意义的字符。
例如:
int a;
scanf(" \t \n %d",&a);
scanf("%d",&a);
两行的scanf语句运行结果一样,但表达效果不同。第1个scanf可以跳过前面的空格符,以消除上一个scanf输入缓冲区的影响
scanf_s
注意:scanf_s是VS中特有的。如果非要在VS中使用scanf需在主函数前加入一行: #define _CRT_SECURE_NO_WARNINGS
scanf和scanf_s的区别
- scanf不会检查输入的边界,因此输入的数据可能会溢出,导致程序出错
- scanf_s更安全,其会检查输入的边界
板块2
gets
函数原型:char *gets(char *string)
函数用法:gets(字符数组或指针);
用法示例:
char m[40];
gets(m);
函数特点:
- gets只能用于接收字符型变量和标点符号。
- gets() 认为空白符(空白符指空格符、回车符、制表符,制表符即Tab键盘)也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以,不管输入了多少个空格,只要不按下回车键,对gets() 来说就是一个完整的字符串。
- gets以换行符结束,并丢弃末尾的换行符添加一个空字符。他解决了scanf以空白符为截至的漏洞。
- 但是gets有一个致命的弱点,虽然gets可以无限读取,但是不会判断上限,因此使用gets不安全,可能会造成溢出
fgets
函数原型:char *fgets(char *s, int size, FILE *stream);
形参说明:s 代表要保存到的内存空间的首地址,可以是字符数组名,也可以是指向字符数组的字符指针变量名。size 代表的是读取字符串的最大长度。stream表示从何种流中读取,可以是标准输入流stdin,也可以是文件流。其中标准输入流就是输入缓冲区。所以如果要从键盘读取数据的话就是从输入缓冲区中读取数据,那么就要从标准输入流 stdin 中读取数据,则第三个参数就为stdin。文件流即从某个文件中读取。PS:初学阶段一般使用stdin(标准输入流)
函数用法:fgets(读入的对象,读入字符的最大数量,要读入的文件);
注意:由于字符串是以 \0 为结束符的,所以fgets()函数的最大读取大小是 size-1
用法示例:
char a[10]; fgets(a,10,stdin); //最大读取大小为9
示例解析:
从stream流中读取size个字符存储到字符指针变量s所指向的内存空间。它的返回值是一个指针,指向字符串中第一个字符的地址。
函数特点:
- 指定读入的字符个数
- 不会将字符串末尾的换行符丢弃再添一个空字符
- 保留多余字符fgets并没有将没有读入的数据丢弃,他们依然在输入缓存区中等待着之后的数据的读取。所以fgets也会有一定的安全隐患
gets和fgets的区别
对比下面两段代码不难发现:gets不能对输入的内容大小做出安全检测,而fgets可以人为的设置输入的大小,间接实现人为的控制程序的安全性。
gets_s
注意:由于gets_s是VisualStudio编译器提供的安全gets函数,所以仅在VS环境底下可以使用。
函数用法:gets_s(字符数组或指针);
用法示例:
char a[40];
gets_s(a);
函数特点:
- 丢弃换行符而不是保存它。对于末尾换行符的处理,在不超过数组范围时 gets_s 丢弃他而不是保存他,这点和 gets一样和 fgets不一样。
- 将多余的字符丢弃当我们向输入缓存区中输入多于数组的字符数时,gets_s会将多余的字符丢弃以达到避免存在安全隐患的目的,不论你是否需要后面的字符。
板块3
getch
函数名:getch
函数用法:getch();
头文件:conio.h
函数原型:int getch(void);
函数功能:无需经过输入缓冲区,直接从标准输入设备(键盘)读入一个字符,而且不会显在显示器上(即不带回显,回显常常指程序开发中执行命令的结果,显示在屏幕上)不以回车为结束,所以当用户按下某个字符时,函数立即读取,无需按回车
注意事项:getch只能读入单个字符,所以数组或字符串不能使用getch
用法示例:
#include <stdio.h>
#include <conio.h>
int main()
{
char m;//:getch只能读入单个字符
printf("请输入一个字符:\n");
m =getch();
printf("你输入的字符为:%c\n",m);
return 0;
}
getche
函数名:getche
函数用法:getche();
头文件:conio.h
函数原型:int getche(void);
函数功能:无需经过输入缓冲区,直接从标准输入设备(键盘)读入一个字符,不以回车为结束,立刻显示在屏幕上(即带回显)
注意事项:getche只能读入单个字符,所以要注意数组或字符串不能使用getche
getchar
函数名:getchar()
函数用法:xxx=getchar(); 或直接 getchar();
头文件:stdio.h
函数原型:int getchar(void);
函数功能:
(1)xxx=getchar();
读取标准输入设备(键盘)输入的字符,并保存在输入缓冲区中。直到用户按回车为止(回车字符也放在缓冲区中)。
(2)getchar();
从缓冲区读走一个字符,相当于清除缓冲区一个字符,包括空白符(空白符指空格符、回车符、制表符,制表符即Tab键盘)
用法示例:
char a[20];
char b[20];
scanf("%c",a);
printf("%d\n",a);
getchar();
gets(b);
printf("%d\n",b);
当前面的scanf()在读取输入时会在缓冲区中留下一个字符'\n'(输入后按回车键所致)所以如果不在此加一个 getchar(); 把这个回车符取走的话, gets() 就不会等待从键盘键入字符,而是会直接取走这个“无用的”回车符,从而程序有误
getch、getche、getchar的区别
- getch和getche功能类似,都是直接从输入设备(键盘)读入单个字符,不经过输入缓冲区直接读取,无需回车。唯一的区别就是getch没有回显,getche有回显
- getch、getche、getchar都只能读取单个字符型变量,其中getchar读取一个字符时剩余字符留在缓冲区
- getch和getche直接从输入设备读取字符,不受缓冲区的影响。而getchar需要从缓冲区读取字符,会受缓冲区(例如回车符)的影响
- getch和getche的头文件为 conio.h 而getchar的头文件为 stdio.h
- 由于功能不同,所以getch、getche和getchar的应用也有所不同
- getchar通常用于清除缓冲区的字符,主要用于清除回车键。
- getch和getche通常用于实现暂停功能。(有的编译器程序执行完结果后的界面不会停下而是一闪就没了。所以在程序最后加上 getch(); 或 getche(); 可以实现暂停功能,让程序停下来)
缓冲区问题 (以scanf为例)
//字符串和数组不需要取地址
char b[30];
scanf("%s",b);//scanf遇到空格会停止编译
printf("%s\n",b);
char c[40];
scanf(" %[^\n]",c);//scanf不再遇空格停止
printf("%s\n",c);
代码分析:scanf(或其它输入函数)从标准输入缓冲区中读取输入的数据,在第一个scanf输入字符后按回车键时的顺序是,先刷新缓冲区,然后再将'\n'放入缓冲区。所以第二个scanf时,它自动把这个回车符('\n')赋给c了。
而如果第二个scanf的输入格式不是%c或%s时,由于格式不匹配,这个回车符会被自动忽略,所以只有在连续输入的%c或%s的格式时才会出现这样的问题。
解决办法:
1.在下一个输入字符之前加一个 fflush(stdin); (C语言提供的缓冲区清空函数)
2.在下一个输入字符之前加一个 getchar(); 以清除缓冲区的回车字符
3.在%号前面加一个空白符(空格符、制表符、回车符)可以抵消前面输入的回车符。因为程序读取时候会自动跳过前面空格符,找到非空白符的数据进行读取(gets不适用)
输入函数总结
- 输入函数大体可分为scanf 、scnaf_s\gets、fgets、gets_s \getch、getche、getchar 三种
- scanf可以接收全部类型的变量,gets等三个只能接收字符串类型的变量,getch等三个只能接收单个字符型的变量
- gets从标准输入设备读取字符串,以回车结束读取,使用'\0'结尾,回车符'\n'被舍弃没有遗留在缓冲区。scanf以空格或回车符结束读取,空格或回车符 会遗留在缓冲区 。
- 只有getch和getche是直接从标准输入设备(键盘)读取数据,其余都是要经过输入缓冲区来读取数据
- scanf和gets都不会对输入的数据内容大小进行判断,所以都有溢出的危险。
- scanf和fgets会保留换行符,而gets则丢弃末尾的换行符添加一个空字符,即将\n改为\0 。解决了scanf以空白符为截至的漏洞。虽然fgets也是保留换行符,但是fgets却没有这个漏洞
- 虽然gets和gets_s用法类似,但gets_s是VS特有的,而且它们的效果并不相同
输出函数
printf
函数用法:printf("<格式化字符串>", <参量表>)
函数特点:printf可以输出任意格式
用法示例:
printf("%d\n",a); puts("hello girl");
puts和fputs
puts函数用法:puts(字符串);
例如puts(x);就相当于 printf("%s\n", x);
fputs函数用法:fputs(s,stream);
其中stdout为标准输出流
fputs 和 puts的区别:
(1)puts() 只能向标准输出流输出,而 fputs() 可以向任何流输出。
(2)使用 puts() 时,系统会在自动在其后添加换行符;而使用 fputs() 时,系统不会自动添加换行符,如输出到屏幕用stdout
用法示例:
char a[10];
char b[10];
fgets(a,10,stdin);
gets(b);
fputs(a,stdout);
puts("b");
注意事项:
puts和fputs函数需要遇到null(即 \0)字符才停止输出。
例如
char a[] = {'H','E','L','L','O'};
char b[] = {'H','E','L','L','O','\0'};
puts(a); //错误
puts(b); //正确
a最后没有\0,所以puts无法识别
putchar
函数用法:putchar(单字符变量);
函数特点:
(1)putchar只能输出任意单个字符
(2)putchar()函数最后不自动加回车符
用法示例:
char ch;
ch = getchar();
putchar(ch);
最后
C语言的输入输出函数肯定远不止这些,这里只是列举了学习C语言前期常用的几个输入输出函数。如果这篇博客还有哪里需要补充和改进的地方,欢迎评论或者私信。