十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
getc(stdin)每次只能从缓冲区读取一个 字符,但是遇到回车符就结束了。连续从缓冲区读取循环此段代码。同样以回车结束。#includestdio.hvoid main(void)
专注于为中小企业提供网站设计制作、成都做网站服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业斗门免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了千余家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
{while(1){
char a;
a=getc(stdin);
printf("%c",a);}} 这是编译器内部定义的函数,他在头文件里的定义可以再函数名上击右键选getc定义查看,但是内部实现看不到。下面是截取了stdio.h里的一段定义。
_CRTIMP int __cdecl _flushall(void);
_CRTIMP FILE * __cdecl fopen(const char *, const char *);
_CRTIMP int __cdecl fprintf(FILE *, const char *, ...);
_CRTIMP int __cdecl fputc(int, FILE *);
_CRTIMP int __cdecl _fputchar(int);
_CRTIMP int __cdecl fputs(const char *, FILE *);
_CRTIMP size_t __cdecl fread(void *, size_t, size_t, FILE *);
_CRTIMP FILE * __cdecl freopen(const char *, const char *, FILE *);
_CRTIMP int __cdecl fscanf(FILE *, const char *, ...);
_CRTIMP int __cdecl fsetpos(FILE *, const fpos_t *);
_CRTIMP int __cdecl fseek(FILE *, long, int);
_CRTIMP long __cdecl ftell(FILE *);
_CRTIMP size_t __cdecl fwrite(const void *, size_t, size_t, FILE *);
_CRTIMP int __cdecl getc(FILE *);//////////////////////////这里定义了getc();
_CRTIMP int __cdecl getchar(void);
getc()中的参数FILE定义:#ifndef _FILE_DEFINED
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
#define _FILE_DEFINED
#endif
在输入语句后使用fflush(stdin);
表示清楚标准输入设备(即键盘)的缓冲区。
就是内存为外设划分了一个输入缓冲区 用于从暂时保存从键盘输入的数据 例如getchar()函数 就要用到
首先从原理上解释一下键盘驱动工作原理,当我们GETCHAR的时候,实际上是用户态程序要求读键盘的内核态的缓冲区(在LINUX系统里其实是个队列),然而这时候该缓冲区内为空(那么该进程SLEEP,也就是您说的屏幕等待状态),这时如果你按下键盘,通过键盘中断驱动程序,把你输入的字符载入内核态的键盘缓冲区(入队列),同时唤醒因为读该缓冲区而SLEEP的进程.
读取该字符后,因为是在内核态,而我们GETCHAR()函数就是把内核态内的数据,转移到用户态,内核台缓冲区因字符被读取而出队列(如果无其它情况,键盘的内核缓冲区队列为空),GETCHAR()函数是通过系统调用来实现的, 该函数最终肯定是以RETURN 来返回结果,而在我们调用GETCHAR()函数后,RETURN的结果以被放入寄存器中.
你首先要明白,从键盘读入键盘缓冲区(buffer)的数据都是以ASCII码存储的(包括回车)。
程序1
#include "stdio.h"
void main()
{
char a;
char b;
scanf("%d",a);
scanf("%d",b);
printf("%d %d",a,b);
}
键盘输入
97回车
第一次回车后,buffer中的ASCII:39h,37h,0AH(0A是换行的ASCII), scanf会根据格式字符串中的第一个%d对buffer按字节顺序读取,当读取到0A时,认为%d型的数据结束,此时把已经读取到的39h,37h依据%d转为整型数据97存储在字符型变量a中。(这里是除去了扫描截止点0AH)
此时buffer中已经无任何数据了。
96回车
第二次回车后,按同样的流程,scanf会根据格式字符串中的第二个%d对buffer按字节顺序读取。最终b得到96.
此时buffer中已经无任何数据了。
输出
97 96
程序2
#include "stdio.h"
void main()
{
char a;
char b;
scanf("%c",a);
scanf("%c",b);
printf("%d %d",a,b);
}
键盘输入
9回车buffer:39H,0AH
因为scanf会按照第一个%c格式扫描buffer(只扫描一个字节就结束),然后把扫描到的39H直接送到变量a(当以%d格式读出来时,39H就是57)
此时,buffer中只有:0AH。
然后,scanft又遇到第二个%c,继续扫描buffer,得到0aH并送入变量b.
此时buffer中已经无任何数据了
输出
57 10
程序3
#include "stdio.h"
void main()
{
char a[100];
char b[100];
scanf("%s",a);
scanf("%s",b);
printf("%s %s",a,b);
}
键盘输入
abc回车
第一次回车后,buffer:61H,62H,63H,0AH。
scanf会按照%s的格式对buffer按字节顺序扫描,当扫描到0AH时,结束扫描(按照%s的要求,空格20H也是扫描结束点)。
然后把扫描到的(除去最后一个判断扫描截至的字节0AH)数据直接送入以a为起始地址的字符串。
此时,buffer无任何数据了。
def回车
第二次回车后,buffer:65H,66H,67H,0AH.扫描的流程与上面的完全一致。
输出
abc def
程序4
#include stdio.h
void main()
{
int i;
char j;
for(i=0;i2;i++)
scanf("%c",j);/*注意这里%前没有空格*/
printf("%d",j);
}
键盘输入
1回车,
这里scanf执行了两次(i==0时,与i==1时),而且每次都是想对j赋值。
第一次scanf,按%c的要求,只扫描buffer中的一个字节,但是buffer中并不数据,于是要求键盘输入数据到buffer,此时的1回车代表向buffer中输入了:31H,0AH。
然后按%c的要求,只扫描buffer中的一个字节:31h,并将它直接送入变量j.
此时,buffer中还留下:0AH。
第二次scanf要求键盘输入数据,按%c的要求,只扫描buffer中的一个字节:0Ah,并将它直接送入变量j.
此时,buffer无数据了。
最后,你用%d格式输出j的值(0AH换成整型就是10)
输出
10
程序5
#include stdio.h
void main()
{
int i;
char j;
for(i=0;i2;i++)
scanf(" %c",j);/*注意这里%前有一个空格*/
printf("%d",j);
}
1回车2enter的情况:
scanf会按照格式控制字符串的要求,顺序扫描buffer.
但是你其中有一个空格,这个很特殊,我也是第一次发现这个问题(一般我都不会在scanf中加入任何常量字符)
我测试了一下:我发现这个空格有吸收回车(0AH)和空格(20H)的“神奇功效”,吸收之后再要求buffer给一个字节,直到这个字节不是0AH或者 20H,此时把这个字节交给下一个格式字串。
第一次循环时遇到格式字串空格,就扫描buffer中的一个字节,但是buffer中无数据,要求从键盘输入数据:1〈回车〉,buffer中有数据了——31H,0AH。再读取到字节31H,scanf发现这个并不是0AH/20H,就把这个字节31H交给格式字符%c处理。
循环结束,此时buffer里面还有:0AH.
第二次循环时遇到格式字串空格,就扫描buffer中的一个字节——0AH,发现是0AH/20H,于是就要求buffer再来一个字节。此时buffer里面已经没有数据了,要求键盘输入:2enter.
buffer中有数据了——32H,0AH。于是再读一个字节31H,scanf发现这个并不是0AH/20H,就把这个字节32H交给格式字符%c处理(j最终得到32H)。
循环结束,此时buffer里面还有:0AH.
这里有一篇关于Printf的帖子:
程序6
#include "stdio.h"
void main()
{
int a;
int b;
scanf("%c",a);
scanf("%c",b);
printf("%d %d",a,b);
}
键盘输入
1回车
问题5:
你的编译器VC认为%d数据应该是4个字节,但是你采用的是%c读数据,
scanf("%c",a);此句读到的是1的ascii码:31h.然后把31H直接送入地址a(而并没有改写a的三个高字节地址)。
scanf("%c",b);同理。
你可以用printf("a=%x,b=%x\n",a,b);来验证我说的。它们的最低字节肯定是31H,0AH。
PS1:
当你把 int a;int b;放在main()外进行定义时,a,b的初值就是0。此时你会得到正确的结果。
当你把 int a;int b;放在main()内进行定义时,a,b不会被初始化(它们的三个三个高字节地址的内容是不确定的),你就会得到上面错误的结果。(定义的动态变量都不会被初始化,静态变量会被初始化为0)
PS2:以下也是不正确的用法。
char c;
scanf("%d",c);/当你用%d给c赋值时,会对从&c开始的连续4个字节进行赋值。当从buffer得到的值是在一个字节范围内(-128~127),下面是可以正常输出的。但是不管怎样,这样做是很危险的——越界。
printf("%d",c);
=================请你测试下这个程序========================
#include "stdio.h"
void main()
{
char c[4],i=4;
scanf("%d",c);/*请输入258回车*/
while(i--0)
printf("%02x ",c[i]);
printf("\n");
}/*如果得到的结果是00 00 00 01 02就说明我的结论是正确的(258的转为16进制数就是00 00 01 02H,然后scanf会把这个数放入以c为起始地址的)
================以下程序也是======================
#include "stdio.h"
void main()
{
char c,i=4;
char *p=c;
scanf("%d",c);/*请输入258回车*/
while(i--0)
printf("%02x ",p[i]);
printf("\n");
}
我们在给函数传多个参数的时候,编译器是从左到右将参数进行压栈的。以你的printf函数来说,把第一个getchar()入栈,然后是第二个。。。这样,最后一个getchar()就处于栈顶,然后执行的话就从栈顶开始取出参数,所以最后一个getchar()最先被执行,所以字符'c'被取走并按照顺序最后打印。这样明白吗?希望对你有所帮助