沧海一粟

驶向大海,乘风破浪

 

对getchar()的探索

     首先弄清楚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 *=__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底层实现的,因为我正在弄这个东东,希望尽快把这个东西弄出来分享给大家。







 

 

 

posted on 2011-10-24 13:11 孺子牛 阅读(632) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

统计

常用链接

留言簿(1)

随笔分类

文章档案(4)

最新随笔

搜索

积分与排名

最新随笔

最新评论