在初学C语言容易忽略的函数-scanf()函数与printf()函数

         一:首先是初学时在使用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;
      }
   
   结果只输出一个a,原因是你在输入a是之后敲了回车,那么a和回车符一起存在缓冲区里面,第一次getchar()之后a被拿走了,所以之后不会再输出了,所以这是需要用到fflush()函数。如果加上注释的那句代码:结果如下



在输入字符数据时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。

例如: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
    #include
      int 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语言的过程中应该注重细节。

有时候小知识点反而会让你眼前一亮。




你可能感兴趣的:(C语言设计)