首先弄清楚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(),如下:
1
char 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(),获取一个字符串。
1
char* 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底层实现的,因为我正在弄这个东东,希望尽快把这个东西弄出来分享给大家。