一:首先是初学时在使用VS软件是对于scanf()和scanf_s()的看法,开始时候因为VS中使用scanf()是一直会报错,所以之后就基本索性都用scanf_s(),至少不会报错,所以以致于后来都忘记去分析这两者的区别。后来仔细回来看看以前学的东西是会发现,其实很多知识点都会隐含在当初认为不起眼的地方。
先来说说scanf_s()函数是Microsoft公司VS开发工具提供的一个功能相同的安全标准输入函数,从vc++2005开始,VS系统提供了scanf_s(),按照官方的说法,是为了安全性方面考虑scanf_s(),这里所谓的安全其实就是在输入的时候进行边界检查罢了。说是这么说,既然是新东西,Microsoft公司是一个喜欢创新的公司,所以趁机展示一下。下面就分析分析详细的区别:
scanf()是一个比较严格的函数,何谓严格,首先函数中是否包含空白/非空白字符对输入格式的影响很大:
比如:scanf("%d,%d",&a,&b); scanf中有一个逗号,所以输入的格式应该是2,3==>a=2,b=3
scanf("%d%d",&a,&b); 可以用空格或回车来分隔两个输入 如 45==>a=4,b=5
scanf("%d %d",&i,&j); 与上面相同
还有:如以下代码:
#define _CRT_SECURE_NO_WARNINGS
#include
int main()
{
char a1 , a2 ;
scanf("%c", &a1);
scanf("%c", &a2);
printf("a1 is %c, a2 is %c", a1, a2);
}
运行该程序, 输入一个字符后A回车 (要完成输入必须回车), 在执行scanf ("%c",&a1)时, 给变量c1赋值"A", 但回车符仍然留在缓冲区内, 执行输入语句 scanf("%c", &c2)时, 变量c2输出的是一空行, 如果输入AB后回车, 那么输出结 果为: c1 is A, c2 is B。
如果要解决上面的问题,那么可以用fflush()函数来解决。如定义函数:int fflush(FILE* stream);
fflush()会强迫将缓冲区内的数据写回参数stream 指定的文件中. 如果参数stream 为NULL,fflush()会将所有打开的文件数据更新.
所以只需要在两个scanf语句中加上fflush()函数即可:
scanf("%c", &a1);
fflush(stdin);
scanf("%c", &a2);
在使用getchar()函数是也会用到fflush()函数:如下面的代码
#include
int main()
{
char sh, ch;
printf("请从键盘输入一个字符\n");
sh = getchar();
//fflush(stdin);
ch = getchar();
printf("输出的字符为:");
putchar(sh);
putchar(ch);
return 0;
}
在输入字符数据时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。
例如:scanf("%c%c%c",&a,&b,&c);
输入为:d e f
则把'd'赋予a, ' ' 赋予b,'e'赋予c。
只有当输入为: def
时,才能把'd'赋于a,'e'赋予b,'f'赋予c。
如果在格式控制中加入空格作为间隔,
如:scanf ("%c %c %c",&a,&b,&c);
则输入时各数据之间可加空格。
使用scanf函数还必须注意以下几点:
1)scanf函数中没有精度控制,如:scanf("%5.2f",&a);是非法的。不能企图用此语句输入小数为2位的实数。
如输入a = 5.2, 则输出a是会遇到问题
2)在输入多个数值数据时,若格式控制串中没有非格式字符作输入数据之间的间隔则可用空格,TAB或回车作间隔。C编译在碰到空格,TAB,回车或非法数据(如对“%d”输入“12A”时,A即为非法数据)时即认为该数据结束。
3)scanf()在读取数据时不检查边界,所以可能会造成内存访问越界,如下面代码:#define _CRT_SECURE_NO_WARNINGS #includeint main() { char buf[] = {'\0'}; scanf("%s",buf); printf("%s",buf); return 0; }
//分配了5字节的空间但是用户输入了10字节,就会导致scanf()读到10个字节//如果输入1234567890,则5以后的部分会被写到别的变量所在的空间上去,从而可能会导致程序运行异常。
从而出现上面的错误窗口。
以上代码如果用scanf_s()则可避免此问题:
char buf[5]={'\0'}; scanf_s("%s",buf,5); //最多读取4个字符,因为buf[4]要放'\0' //如果输入1234567890,则buf只会接受前4个字符
注: scanf_s最后一个参数n是接收缓冲区的大小(即buf的容量),表示最多读取n-1个字符.
PS: 很多带“_s”后缀的函数是为了让原版函数更安全,传入一个和参数有关的大小值,避免引用到不存在的元素,防止hacker利用原版的不安全性(漏洞)黑掉系统。
二:其次是刚开始学就知道的函数printf(),虽然大家都知道这是一个输出函数,但是真要仔细研究一下,里面还是有地方的可以让你感觉挺有趣的。
如以下关于printf的小代码:
#include
int main()
{
char arr1[] = { 'h', 'e', 'l', 'l','o' };
char arr2[] = "hello";
printf("%s\n", arr1);
printf("%s\n", arr2);
return 0;
}
输出的结果为:
原因是printf()函数只有遇到'\0', 才会停止输出,所以第一个输出后面是乱码,而第二个则刚好输出“hello”(hello中包含'\0')
如果将以上代码稍加修改,改为如下:
#include
int main()
{
char arr1[6] = { 'h', 'e', 'l', 'l','o' };
char arr2[] = "hello";
printf("%s\n", arr1);
printf("%s\n", arr2);
return 0;
}
则输出结果又变为:
原因是在arr1[]数组中自动填充了一个数字0,(相当于‘0’),因此输出了“hello”
如果再将代码修改一下,如:
#include
int main()
{
char arr1[6] = { 'h', 'e', 'l', 'l','o','0' };
char arr2[] = "hello";
printf("%s\n", arr1);
printf("%s\n", arr2);
return 0;
}
那么输出结果又会有变化么?
答案是肯定的!
可看到arr1[]数组中又出现了乱码,原因是我刚刚加了一个数组元素'0',但是‘0’与‘\0’与数字0不同,因此printf()函数还是会继续往后读取内容。从而又出现乱码。
其实仔细想一想,有时候很多有趣的知识都从最不起眼的地方发现的,所以我们在学习C语言的过程中应该注重细节。
有时候小知识点反而会让你眼前一亮。