getchar的研究

首先,getchar是一个宏,它的宏定义如下:
#define getchar()  getc(stdin)
#define getc(f) \
((--((f)->level) >= 0) ? (unsigned char)(++(f)->curp)[-1] : \
_fgetc (f))

由上可以看出,getchar是getc()的宏,而getc()又是一个宏,通过代入流stdin进入宏展开;stdin:输入流,从键盘输入到内存,又符合文件操作的一些概念,所以用到了FILE的一些内容,不然,又如何解释宏里面的level和curp呢?

接着,进入到FILE的结构,从头文件中查看:
typedef struct {
short level; /* fill/empty level of buffer */
unsigned flags; /* File status flags */
char fd; /* File descriptor */
unsigned char hold; /* Ungetc char if no buffer */
short bsize; /* Buffer size */
unsigned char *buffer; /* Data transfer buffer */
unsigned char *curp; /* Current active pointer */
unsigned istemp; /* Temporary file indicator */
short token; /* Used for validity checking */
} FILE; /* This is the FILE object */

一堆英文单词,大概了解到level表示满/空的程度,而curp是当前活动指针的位置;

联想到键盘输入,每次从键盘输入一个字符时,字符进入输入流缓冲区,程序怎么知道缓冲区还有多少个字符呢?可能就要借助这个level,来表示缓冲区中还有多少字符,每取走一个,level--,而指针的curp就前进一个位置;

这样就可以顺理理解上面的宏定义,首先判定--stdin->level的大小,意思是,如果缓冲区空了,就不能取了,如果不空,就把指针先++,前进一格,再取前面的值;这个步骤是先往前走,接着再取,很像队列;

举例比较容易理解:
while((c=getchar())!=‘#‘)分析这句代码;
首先调用getchar()这个宏,getchar()又调用getc(stdin)传入到宏中展开,先判定--stdin->level,现在没有输入任何值,缓冲区肯定为空的,则level为0,再自减就为-1,肯定不会>=0,则前半段不执行,执行后半段的_fgetc(stdin),这个貌似内部函数,不管怎么样,它在控制台等待用户输入;不然,那一动一动的光标从哪来呢?

用户输入:programming#,回车就表示输入完成,接着再进入c=getchar()进行取值,缓冲区已经存入内容,所以后半段不再执行,执行前半段;而curp,也就是当前指针,应该指向的是programming#的第一个字符p;

接着,++stdin->curp,指向r,再取前面的值stdin->curp[-1],也即p,而curp自然又指向未取的第一个字符,这样就解释了上面宏中的定义;

while循环向下执行,直到取走‘#‘号时,循环中止,缓冲内还剩下一个回车符,level自然就为1;

比如:c=getchar();c=getchar();程序输入a 回车,最后c得到的是回车符,之后,level=0;

似乎已经完了,可是接下来会出现一个问题,如果有这样的程序,c=getchar(),一不小心输入一串字符,那缓冲区肯定还有剩余的字符,如果再用其它从输入流缓冲区中取值的函数,可能会把剩余的字符取走;

比如说,c=getchar();gets(str);

gets也从输入流中取值,这样巧合很少,但也有这种可能,这并非想要的值;于是,又产生了一个fflush(FILE *stream)函数,把流清空,这样也合理了;

问题是fflush(stdin)这样的函数,具体又是如何工作的呢?真的把流“清空”了吗?我宁愿相信它是把level置为0,也不愿相信它把内存清空了,事实上,那些输入的值还在;

再以上面的例子,while((c=getchar())!=‘#‘),似乎到最后已经取完了,假如输入:programming#,取走#之后,curp指向回车符这个值,我要把这个curp往前拉回来,执行:--stdin->curp,再--stdin->curp,按字面上,它应该指向字符‘g‘,可是再取还是取不到值,原因是此时level已经为0了,做了限制,于是再强制的把level赋值,比如stdin->level+=2;这样一来,好像还有两个字符没有取,接着,c=getchar(),还是可以取到字符‘g‘,这说明,内存中的数据一直都存在;

    char c;
    c=getchar();
    c=getchar();
    printf("%d\n",c);
    --stdin->curp;
    --stdin->curp;
    stdin->level+=2;
    c=getchar();
    printf("%c\n",c);

这个是关于上面的代码;

另一个比较有意思的是,缓冲区中的流不仅可以取出,也可以退回去;这个函数是ungetc(int ch,FILE *stream);

按照上面的推理,每取走一个字符,curp先++,然后再取前面的字符;如果回退一个字符,这个字符退到什么地方呢?

经过实验,字符退到curp后面,也即是已经取走的那个字符的位置;举例来说;

abcd#,假如已经取走c,则curp最后实际上指向的是字符d,如果再回退一个字符,比如:ungetc(100,stdin),则ASCII码为100的字符会退到c的位置,但是不仅仅是退回一个字符,curp和level都随之变化,这也是情理之中的事儿,结果是:--curp;++level;

curp依然指向当前未取的字符;(它假定这个退回来的字符是未取的);

除了getchar(),gets(),从缓冲区中取值的还有很多函数,比如scanf()等等;

scanf()每次从缓冲区中取值,取决于格式,也即是程序要求它取什么,比如取整型数,则scanf()一直扫描缓冲区,前面的空格忽略,一直到数值型,而在后面,遇到字符或者空格时,就截断;

例:int n;scanf("%d",&n);

最后,做一个程序,该程序从控制台一行中,输入任意个空格和整数,计算输入的整数和;

例如输入:78 98 32 11 10...

这个程序主要考虑如何过滤空格,假如用c=getchar()来判定,如果取到了非空格的值怎么办呢?这就用到上面的ungetc(),如果不是空格(数值型)就退回到流中,让scanf()来取;

int var,sum=0;
while(scanf("%d",&var))
{  
  sum+=var;
  while((c=getchar())==‘‘); /* 过滤空格 */
  if(c==‘\n‘)break;
  ungetc(c,stdin);
};

全文结束
  

时间: 2024-10-19 19:30:04

getchar的研究的相关文章

今天研究了一下手机通信录管理系统(C语言)

题目:手机通信录管理系统 一.题目要求 二.需求分析 三.设计步骤/编写代码 四.上机/运行结果 五.总结 一.题目要求 模拟手机通信录管理系统,实现对手机中的通信录进行管理操作.功能要求: (1)查看功能:A:办公,B:个人,C:商务 (2)增加联系人:录入新数据(姓名,电话,分类,邮箱:weiyang,153********,个人,[email protected]) (3)修改功能:选中某人的姓名,可对其数据进行修改操作 (4)删除功能:选中某人姓名,可对此人的相应数据进行删除,并自动调整

[bzoj4241][历史研究] (分块)

Description IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. 日记中记录了连续N天发生的时间,大约每天发生一件. 事件有种类之分.第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大. JOI教授决定用如下的方法分析这些日记: 1. 选择日记中连续的一些天作为分析的时间段 2. 事件种类t的重要度为t*(这段时间内重要度为t

Project:网络导通概率的研究

最近老师给了一个题目,说是研究一个正常矩阵任意概率置点概率下,双向导通(x,y)的概率(要求有自然边界条件,也就是可以从0->length-1),用代码敲了一下demo,结果发现有个好有趣的结果. 那就是:不同大小的矩阵,导通概率从某一个概率上升,点越多,上升程度越快(斜率越大),但是都会(0.592...,35%)左右..符合ziff论文的结果 下面是demo,没有什么特别的算法,也就是DFS稍微优化一下 1 #include <iostream> 2 #include <fun

C语言中getch()、getche()和getchar()

本文章为转载文章,文档贡献者wdzhangxiang 个人网址:www.baidu.com/p/wdzhangxiang 首先不要忘了,要用getch()必须引入头文件conio.h,以前学C语言的时候,我们总喜欢用在程序的末尾加上它,利用它来实现程序运行完了暂停不退出的效果.如果不加这句话,在TC2.0的环境中我们用Ctrl+F9编译并运行后,程序一运行完了就退回到TC环境中,我们根本来不及看到结果,这时要看结果,我们就要按Alt+F5回到DOS环境中去看结果,这很麻烦.而如果在程序的结尾加上

几种空间分割算法研究之bsp

BSP: 二叉分割树,是一种分割场景的方法,下面代码是BSP树一种实现可运行: 运行例子中,将定义的16个空间面,分割为一个深度是3的BSP树,上图显示的是运行结果: #include "stdafx.h" #include <map> #include <vector> #include <iostream> using namespace std; //定义空间的点结构 struct point { float x,y,z; point():x(

bzoj 4241 历史研究

Description IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. 日记中记录了连续N天发生的时间,大约每天发生一件. 事件有种类之分.第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大. JOI教授决定用如下的方法分析这些日记: 1. 选择日记中连续的一些天作为分析的时间段 2. 事件种类t的重要度为t*(这段时间内重要度为t

cin.get(),cin.getline(),getline(),gets(),getchar()

----------------------- 1.cin.get() (1).cin.get()提取单个字符,可以提取回车.空格 a=cin.get(); (2)同(1) cin.get(a); (3). cin.get(字符数组,字符个数n,终止字符);//终止字符可以不写,默认为'\n' 或cin.get(字符指针,字符个数n,终止字符);//终止字符可以不写,默认为'\n' 不可跳过终止符号.需把回车'\n'提取掉 可以写成 cin.get(a,20); cin.get(); 也可以组合

研究系统IO和glib IO的关系

PS:这里的缓冲与非缓冲的区别是相对于用户进程,下文的“非缓冲文件系统”指用户的程序没有缓冲区,不要误解为系统没有缓冲区. 1.缓冲文件系统缓 冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”, 装满后再从内存“缓冲区”依此读入接收的变量.执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件.由此可以看出,内存 “缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的

C++学习研究之一— —输入输出

cout 是在标准库中定义的输出流对象,‘<<’是重定向符. endl是叫操纵符,其实是个模板函数,在输出流中写入换行符,并且刷新输出缓存,而'\n'只是往输出流中写入了换行符,没有刷新输出缓存,在输出到屏幕时,没有明显的区别,但是如果写入到文件时,'\n'只是把内容写道了缓存中,并没有写入到磁盘,而endl则把内容写入到了磁盘. 如果两个加引号的字符数组中间没有除空格和注释以外的字符,预处理器会自动把两个字符数组连接起来,并成一个字符数组.因为预处理器会去掉空行.空格.和注释,所以两个字符数