首先弄清楚getchar是怎么工作的吧。调用getchar的时候获取一个字符的时候,其并不是直接等待键盘的输入,而是直接去缓冲区读取自己需要的字符,若缓冲区为空,才等待键盘输入,并且将ENTER作为输入的结束标志,而输入的所有字符(往往不止一个)都保存在缓冲区中,返回第一个输入的字符。
下面探究一下getchar的底层实现:
=======================================================
1.getchar()
#define getchar() getc(stdin)
__checkReturn int _cdecl getchar(void)
可以得到,getchar()是一个宏,而真正的函数是getc().而getc()和stdin又是什么东西呢?
2.getc()
_CRTIMP __checkReturn int __cdecl getc(__inout FILE * _File);
原来getc()是一个返回int的函数。FILE是什么东东呢?
3.stdin
#define stdin (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])
stdin也是一个宏,&__iob_func()[0]是虾米?
4.FILE
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
FILE是一个结构体,,,有点晕了。。。坚持!
5.__iob_func()
_CRTIMP FILE * __cdecl __iob_func(void);
原来的__iob_func()是一个返回FILE*的函数。
6.(&__iob_func()[0])
经过很痛苦的过程,绞尽脑汁的空想,函数指针和函数指针数组,结构体成员的下标访问测试,终于的出来,调用__iob_func()返回FILE*,对其取下标运算[0],得到FILE结构体中的char* _ptr,最后取地址得到_ptr的地址。
由上面可以推测,getc()从FILE结构体中的_ptr读取字符。进一步查询FILE中每一个变量的含义,可以得知_ptr指向当前读取的字符,并且每读取一个字符向前步进1.知道了这些,但是getc(stdin)到底是如何实现的呢?由于没有源码,只好自己根据getchar的工作原理自己实现一个GetChar(),如下:
1char GetChar()
2{
3 FILE *f =__iob_func(); //获取当前实例的FILE*
4 if ('\0'==*(f->_ptr)) //缓冲区没有数据或者已经全部被读完,则等待外部输入
5 {
6 while(true)
7 {
8 char c = getch(); //借助getch获取键盘输入,并且以ENTER作为输入结束标志
9 if (13==c)//虽然'\n’的ascii码值为10,但是getch接收到的ENTER是’\r’,为13,所以需要转码
10 {
11 *(f->_base+f->_cnt)='\n';
12 f->_cnt++;
13 *(f->_base+f->_cnt)='\0';
14 printf("\n");
15 break;
16 }
17
18 *(f->_base+f->_cnt)=c;
19 f->_cnt++;
20 *(f->_base+f->_cnt)='\0';
21 printf("%c",c);
22
23 }
24 f->_ptr++; //每次读取完一个字符_ptr向后步进1
25 return *(f->_ptr-1); //返回上一个字符
26 }
27 else //缓冲区仍有数据
28 {
29
30 f->_ptr++;
31 return *(f->_ptr-1);
32 }
33
34 return –1;
35}
36
37
经过测试,目前仍然能够正常工作。其实我们也可以不用头文件提供的FILE结构体,自己写个更简单的结构体,用以保存我们的输入。
然而在这个函数中,我使用了getch(),这个库函数,它的工作原理是:检测键盘输入,只要有输入,立即返回对应的ascii码值,并且不会回显到屏幕。所以如果预备继续深入,还要探究getch()的底层实现,希望能有志同道合的朋友和我一起研究。
写完这个GetChar,在此基础上既而实现了自己的GetString(),获取一个字符串。
1char* GetString()
2{
3 char c;
4 char *space=(char*)malloc(sizeof(char)*100);
5 char *t=space;
6 while (true)
7 {
8 c=GetChar();
9 if (c=='\n')
10 {
11 *t='\0';
12 break;
13 }
14 *t++=c;
15 }
16
17 //重新分配空间,节约空间
18 char* result=(char*)malloc(sizeof(char)*(t-space+1));
19 strcpy(result,space);
20 free(space);
21
22
23 return result;
24}
25
26
以上代码都通过了自己的测试(VS2005)。
最后呼吁一下,希望能有志同道合的朋友和我交流关于C库函数底层实现方面的知识,特别是懂getch底层实现的,因为我正在弄这个东东,希望尽快把这个东西弄出来分享给大家。