C语言中:getc(stdin) 能取得键盘缓冲区中的一个字符:
如:
#include<stdio.h>
void main(void)
{
char a;
a=getc(stdin);
printf("%c",a);
}
以下是stdio.h 中的部分源码:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
_CRTIMP int __cdecl _filbuf(FILE *);
#define getc(_stream) (--(_stream)->_cnt >= 0? 0xff & *(_stream)->_ptr++ : _filbuf(_stream))
#define stdin (&_iob[0])
#define stdout (&_iob[1])
#define stderr (&_iob[2])
_CRTIMP int __cdecl getc(FILE *);
_CRTIMP extern FILE _iob[];
从上面我们知道,getc()的参数是 一个可以指向 FILE 类型的变量,同时也知道 getc()的实现
也知道 getc(stdin) 中的stdin 是(&_iob[0])。
所以我们知道 :
_stream=&_iob[0]; 从上面我们知道_iob[] 是一个FILE数组,
a=getc(stdin); 当程序执行到这里时,是怎么确定 _iob[0]._cnt的值的呢?
即:
问题1: --(_stream)->_cnt >= 0 中(_stream)->_cnt的值是那里来的?
问题2:0xff & *(_stream)->_ptr++,_ptr _ptr值是怎么来的?
问题3:_filbuf(_stream)) 是什么意思?
主要要问:
getc() 是怎么实现从 键盘缓冲区中取得一个字符的?
问题一:
首先得搞清楚stdin是什么:
#define stdin (&_iob[0])
然后再看_iob[0]:
#define _IOB_ENTRIES 20
typedef struct _iobuf FILE;
FILE _iob[_IOB_ENTRIES] = {
{ _bufin, 0, _bufin, _IOREAD | _IOYOURBUF, 0, 0, _INTERNAL_BUFSIZ },
{ NULL, 0, NULL, _IOWRT, 1, 0, 0 },
{ NULL, 0, NULL, _IOWRT, 2, 0, 0 },
};
这是一个全局的FILE结构体数组。肯定在初始化的时候就已经成这样了。
所以第一个问题解决。
问题二也就解开了。
问题三:
_filbuf(_stream)) 中_filbuf是个函数,_stream是stdin也就是FILE _iob[0].
再看获取IO输入的调用流程:
getc的整个流程:
.exe!main()
.exe!_filbuf(_iobuf * str=0x0043d1c0) // #define getc( param ) _filbuf( param )
.exe!_read(int fh=0, void * buf=0x0043e800, unsigned int cnt=4096)
kernel32.dll!_ReadFile@20()
kernel32.dll!_ReadConsoleA@20()
kernel32.dll!_ReadConsoleInternal@32()
ntdll.dll!_CsrClientCallServer@16()
ntdll.dll!_ZwRequestWaitReplyPort@12()
ntdll.dll!_KiFastSystemCall@0()
_KiFastSystemCall@0()的反汇编代码是:
mov edx, esp
sysenter // 进驱动
因此 _filbuf就负责一直等待console的输入缓冲区里面有字符输入,然后填到FILE的buf里面,也就是(_stream)->_ptr所指向的这个缓冲区.
(_stream)->_cnt就是在filbuf里面设置的,读取了io,填到buff里面后,会修改这个cnt,表示buff里面有多少字符.