前述:
第一条记:
最近在海纳百川的地铁上,脑海中常想起魏源说过一句话"师夷长技以制夷". 联想最近中国和日本的态度,再联想起自己的人生经历,可以说我是看日本动漫,打日本游戏长大的,到现在看的工具书中绝大部分是老美和日本的.还有一个那就是看金庸和古龙所代表的武侠江湖梦.总结就是,在日本文化我学到了坚强,永不服输.老美的文化给了我探求科学的阶梯.中国的文化给我烙印上了骨气,我一直认为最后着是最重要的.再总结就是"用别人的长处征服别人,用自己的长处统治别人"
第二条记:
我的好朋友网视悠悠教给我与人处态度"宁可人负我,我绝不负人",我再联想我那‘老乡‘阿瞒说的一句话"宁教我负天下人,休教天下人负我".一合并,就是"宁教天下人负我,休教我负天下人".我觉得很霸气很对.
第三条记:
最后我想起了,雷峰同志笔记中记叙的一句话:"对待同志要像春天般的温暖,对待工作要像夏天一样火热,对待个人主义要像秋风扫落叶一样,对待敌人要像严冬一样残酷无情.".用他的思想扩展一下,我领悟的是"接受变化,敢于变化,必须变化".举个例子就是:对待日本,采用第一条记,别人=日本.对待中国,采用第二条记,天下人=中国.有点不冷静了,估计是我恨我自己,这么快就忘记了自己的锐气!
正文:
最经看到下面一段程序(出自<<征服C指针>>,[日]前桥和弥,吴亚明 译):
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char buf[256];
int size;
int *variable_array;
int i;
printf("Input array size>");
fgets(buf,256,stdin);
sscanf(buf,"%d",&size);
variable_array=malloc(sizeof(int)*size);
for(i=0;i<size;i++)
{
variable_array[i]=i;
}
for(i=0;i<size;i++)
{
printf("variable_array[%d]..%d\n",i,variable_array[i]);
}
return 0;
}
发现些这个程序的作者绝对是个另类的程序员,它发现了C中scanf()函数不安全,存在输入缓存问题.
scanf()函数缓存问题描述如下:
while(scanf("%d",&hoge)!=1)
{
fprintf(stderr,"Input error, please enter again!\n");
}
上面的代码只要输入出错一次,就进入了死循环.导致BUG就在于,输入的缓存问题.解决这个问题的办法就是,消除缓存.写上面的作者
显然也是考虑到了这个问题,采用了下面的方法
char buf[256];
int size;
fgets(buf,256,stdin);
sscanf(buf,"%d",&size);
读取一串数据到第一参数中,在数据少于256个ASII字符的情况下都没问题.再从buf字符串中格式化读取数据到size中.这种解决缓存的方法,很巧妙, 用缓存来读缓存,来消除缓存.但是对于严谨的程序而言这显然是不行的.当用户输入的数据超出了你所设置的缓存阀值,并注入了一段破坏的代码,怎么办.这个问题高桥和弥也说的很详细,先列举网络上的蠕虫病毒,最后又说256的缓存够了.他很严谨,但他仍然保留了这个错误,用小日本的话说,这是不可原谅的.
我的解决办法如下,设计原则是不相信用户(的输入).
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
int main(void)
{
int size;/*数组大小*/
int i;/*下标的指示*/
int *variable_array;/*储存数据的数组*/
int ch;/*清除缓存用的临时变量*/
int hoge=0;/*记录用户输入变量是否和法*/
do
{
printf("Input array size>");
hoge=scanf("%d",&size);/*获取用户输入的变量*/
if(hoge<=0||size<=0)
{
printf("You input is error!\n");
printf("You need to enter again.\n");
hoge=0;/*清空接收的值*/
}
while((ch=getchar())!=10&&ch!=EOF);/*这只适用于控制台和用t打开的文件*/
}while(hoge<=0);
/*下面是合法情况,声明空间*/
variable_array=malloc(size*sizeof(int));
/*为数组赋值*/
for(i=0;i<size;i++)
{
*(variable_array+i)=i;
}
/*输出刚才赋的值*/
printf("The following array data:\n");
for(i=0;i<size;i++)
{
printf("variable_array[%d]...%d \n",i,variable_array[i]);
}
getch();
return 0;
}
解决的方法就是利用
int ch;
while((ch=getchar())!=10&&ch!=EOF);
上面的代码消除输出缓存
再根据
if(hoge<=0||size<=0)
{
printf("You input is error!\n");
printf("You need to enter again.\n");
hoge=0;/*清空接收的值*/
}
判断用户输入是否合法,不断的继续循环,利用getchar()吃掉多余的缓存,直到用户输入正确为止.
封装:
上面方法虽然解决BUG,但是感觉代码耦合性太强,能不能分离封装一下呢.当然是可以的,下面先封装一个清空输入缓存的
方法
/*
*描述:清除控制台输入流
*函数声明:void clear_input_cache(void)
*函数参数:void
*返回值:void
*使用例子:
* int hoge;
* scanf("%d",&hoge);
* clear_input_cache();
*/
void clear_input_cache(void)
{
int ch;
while((ch=getchar())!=10&&ch!=-1);
}
但是感觉scanf()函数能够获取数据,但是第一次输入错了,还有第二次输入的机会怎么办呢.继续封装scanf()
封装代码如下:
#include <stdarg.h>
/*
*描述:安全的scanf,它会在用户输出错误的情况下,给予提示,让用户重新输入
* 但是,还是存在scanf同样的BUG比如想得到%d数据,你输入了123ads也是可以的.
*函数声明:void security_scanf(int variable_count,char *format,...)
*函数参数:
* int variable_count;表示需要要接收多少个参数,这个参数是安全输入的关键.
* char *format;接收数据的格式
* ...:表示接收数据的变量,可变参数
*返回值:void
*使用例子:
* int hoge,piyo;
* security_scanf(2,"%d %d",&hoge,&piyo);
*/
void security_scanf(int variable_count,char *format,...)
{
int scn_count=0;/*记录用户输入的变量*/
va_list arg_list;
for(;;)
{
va_start(arg_list,format);
scn_count=scanf(format,arg_list);
va_end(arg_list);
/*如果接收成功就直接结束循环*/
if(variable_count==scn_count)
break;
/*如果输入出错,先清空输入流,再重新开始,并给出用户提示*/
clear_input_cache();
printf("You input is error!\n");
printf("You need to enter again.\n");
printf("You input data>");
}
}
利用上面封装方法,重新写一个程序,来解决scanf()输入缓存的问题.代码如下,说一些题外话,我没有‘赶尽杀绝‘,总是消灭输入
缓存.将scanf和clear_input_cache捆绑在一起,因为编程有的时候也需要输入缓存,例如可以输出缓存来分析程序出错的地方在哪.
而且scanf函数也能通过设置特殊的读取的格式来清除输入缓存.所以上面的方法只是一种参考,还有更多的方法.
自由在读者自己.自由也是C的一大乐趣吧.
下面是利用上面封装的方法写的程序:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <stdarg.h>
int main(void)
{
int size;/*数组大小*/
int i;/*下标的指示*/
int *variable_array;/*储存数据的数组*/
for(;;)
{
printf("Input array size>");
security_scanf(1,"%d",&size);
if(size>0)/*0这个值是否保留,值得讨论,在这里我就排除0了*/
break;
printf("Input array size is error!");
printf("He can‘t less than and equal to zero!");
}
/*下面是合法情况,声明空间*/
variable_array=malloc(size*sizeof(int));
/*为数组赋值*/
for(i=0;i<size;i++)
{
*(variable_array+i)=i;
}
/*输出刚才赋的值*/
printf("The following array data:\n");
for(i=0;i<size;i++)
{
printf("variable_array[%d]...%d \n",i,variable_array[i]);
}
getch();
return 0;
}
其实观看上面封装好的代码,还是有点不爽的,代码还是比较多.这没真没办法了.其实关键是scanf()函数支持可变参数,
继续封装起来比较麻烦,否则如果参数固定,直接加一个void *指针接收函数指针来处里.那样封装起来就更爽了.当然我觉得
封装的越好,用起来就越爽,速度就慢下来了,效率就低了.
扩展:
下面展示一下scanf()函数源码,来自于Microsoft的C++,源自scanf.c文件中
/***
*scanf.c - read formatted data from stdin
*
* Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
* defines scanf() - reads formatted data from stdin
*
*******************************************************************************/
#include <cruntime.h>
#include <stdio.h>
#include <dbgint.h>
#include <stdarg.h>
#include <file2.h>
#include <internal.h>
#include <mtdll.h>
/***
*int scanf(format, ...) - read formatted data from stdin
*
*Purpose:
* Reads formatted data from stdin into arguments. _input does the real
* work here.
*
*Entry:
* char *format - format string
* followed by list of pointers to storage for the data read. The number
* and type are controlled by the format string.
*
*Exit:
* returns number of fields read and assigned
*
*Exceptions:
*
*******************************************************************************/
/*
* stdin ‘SCAN‘, ‘F‘ormatted
*/
int __cdecl scanf (const char *format,...)
{
int retval;
va_list arglist;
va_start(arglist, format);
_ASSERTE(format != NULL);
_lock_str2(0, stdin);
retval = (_input(stdin,format,arglist));
_unlock_str2(0, stdin);
return(retval);
}