今天在学习《The C Programming Language》是遇到了一个问题,就是在使用getchar()函数是遇到了不能理解的问题,书上有个例子:
#include <stdio.h>
main()
{
If you want to read a single character from standard input, you can use the
#include <stdio.h>
main()
{
int c;
while ((c=getchar())!=EOF)
putchar(c);
}
while ((c=getchar())!=EOF)
putchar(c);
}
在GCC编译器上编译运行后,输入abc,回车后输出abc。但如果按照getchar和putchar只读入和输出一个字符的定义来看,这就出现了问题。但是细想一下,实际系统中需要考虑终端输入输出的具体实现,当读入输入流遇到回车时该行才算输入完毕,但是getchar仍然是一个一个字符的读入,赋给c,然后执行一个putchar,但是执行putchar时由于输入过程还没有完成,putchar只能将需要输出的值压到stdout中,等待输出,当输入过程完毕,此时执行由于输入输出实现的局限,被延迟的输出流,所以一下子将输出流中的数据全部输出来。
此处还要提到的是,将c定义为int类型而不是char是有原因的。在没有数据输入的时候,getchar返回一个特殊值,该值即为EOF(end of file),该值在stdio.h头文件中已经定义为-1,倘若将c定义为char,则不能存贮EOF,此时会发生溢出的处理,这就会根据不同的机器实现而定了,这将出现不可控的情况。
对于EOF有下面两点总结:
1.EOF作为文件结束符时的情况:
EOF虽然是文件结束符,但并不是在任何情况下输入Ctrl+D(Windows下Ctrl+Z)都能够实现文件结束的功能,只有在下列的条件下,才作为文件结束符。
(1)遇到getcahr函数执行时,要输入第一个字符时就直接输入Ctrl+D,就可以跳出getchar(),去执行程序的其他部分;
(2)在前面输入的字符为换行符时,接着输入Ctrl+D;
(3)在前面有字符输入且不为换行符时,要连着输入两次Ctrl+D,这时第二次输入的Ctrl+D起到文件结束符的功能,至于第一次的Ctrl+D的作用将在下面介绍。
其实,这三种情况都可以总结为只有在getchar()提示新的一次输入时,直接输入Ctrl+D才相当于文件结束符。
2.EOF作为行结束符时的情况,这时候输入Ctrl+D并不能结束getchar(),而只能引发getchar()提示下一轮的输入。
这种情况主要是在进行getchar()新的一行输入时,当输入了若干字符(不能包含换行符)之后,直接输入Ctrl+D,此时的Ctrl+D并不是文件结束符,而只是相当于换行符的功能,即结束当前的输入。
EOF虽然是文件结束符,但并不是在任何情况下输入Ctrl+D(Windows下Ctrl+Z)都能够实现文件结束的功能,只有在下列的条件下,才作为文件结束符。
(1)遇到getcahr函数执行时,要输入第一个字符时就直接输入Ctrl+D,就可以跳出getchar(),去执行程序的其他部分;
(2)在前面输入的字符为换行符时,接着输入Ctrl+D;
(3)在前面有字符输入且不为换行符时,要连着输入两次Ctrl+D,这时第二次输入的Ctrl+D起到文件结束符的功能,至于第一次的Ctrl+D的作用将在下面介绍。
其实,这三种情况都可以总结为只有在getchar()提示新的一次输入时,直接输入Ctrl+D才相当于文件结束符。
2.EOF作为行结束符时的情况,这时候输入Ctrl+D并不能结束getchar(),而只能引发getchar()提示下一轮的输入。
这种情况主要是在进行getchar()新的一行输入时,当输入了若干字符(不能包含换行符)之后,直接输入Ctrl+D,此时的Ctrl+D并不是文件结束符,而只是相当于换行符的功能,即结束当前的输入。
下面是一段从
The GNU C Programming Tutorial上面的解释:
getchar
If you want to read a single character from standard input, you can use the
getchar
function. This function takes no parameters, but reads the next character from
stdin
as an
unsigned char
, and returns its value, converted to an integer. Here is a short program that uses
getchar
:
#include <stdio.h> int main() { int input_character; printf("Hit any key, then hit RETURN.\n"); input_character = getchar(); printf ("The key you hit was '%c'.\n", input_character); printf ("Bye!\n"); return 0; }Note that because
stdin
is line-buffered,
getchar
will not return a value until you hit the <RETURN> key. However,
getchar
still only reads one character from
stdin
, so if you type
hellohellohello
at the prompt, the program above will still only get once character. It will print the following line, and then terminate:
The key you hit was 'h'. Bye!
从该段解释中可以看出,在键盘输入的情况下,getchar按照“行”进行读取,直到遇到回车键后返回,但是这些键盘的输入首先是被存进了stdin(标准输入流)的缓冲区,由getchar函数的定义可知,它只是返回一个字符,所以返回的只是第一个字符,符合函数的定义。
但有的时候在程序中使用getchar()会出现貌似该调用没有执行的假象,直接跳过了。这种情况主要还是由于stdio流的问题,由于在输入的时候总要遇到回车才能结束输入,在有的情况下回车键也被作为一个字符存入了输入流的缓冲区,然后紧接着调用getchar后,就返回了回车键,这样就相当于直接读取了一个空行,读取完毕当然返回,就导致了好像没有执行的假象。下面的代码是执行一个十六进制字符串转换为数值的代码。
#include <stdio.h>
double htoi(char s[]);
int main(void)
{ char s[100];
int ch='Y';
double data;
while(ch=='Y'||ch=='y')
{
printf("please input the data begin with 0x or 0X,the number can be 0~9,a~f,A~F\n");
scanf("%s",s);
data=htoi(s);
if (data!=-1)
printf("%s equals %.0f\n",s,data);
else printf("wrong data\n");
printf("try again (Y or y),else for quit\n");
//
fflush(stdin);
ch=getchar();
if (ch=='\n')
{
printf("ch is a return key\n");
/*scanf的最后一个回车仍然保留在输入流中,所以当getchar时直接将该回车返回了,解决办法是清空输入流*/
ch=getchar();
}
}
return 0;
}
最初调试时始终没有实现原来的意图,getchar没有实现原来的功能,添加了下面对ch判断是否为回车符的判断后发现最后的ch确实是回车符.
解决这个问题的方法就是在最后那个getchar调用之前先清空输入流,
fflush(stdin),其实该语句并不是标准语句,但是它实现了将输入流中的数据丢弃的操作,使得后面的getchar可以等待用户输入一个字符。
从本例中我初步体会到了标准输入输出流的一点概念,积累了一定的经验。
本文大多经过本人的调试推敲,同时参考了 chinaunix上的一篇文章
本文大多经过本人的调试推敲,同时参考了 chinaunix上的一篇文章