📄 gets_flush1.html
字号:
<html><!-- Mirrored from c-faq.com/stdio/gets_flush1.html by HTTrack Website Copier/3.x [XR&CO'2008], Sat, 14 Mar 2009 08:01:11 GMT --><head><title>fflush vs gets</title></head><body>From: Chris Torek<br>Subject: Re: <TT>fflush</TT> vs <TT>gets</TT><br>Date: 2000/01/20<br>Message-ID: <clcm-20000120-0031@plethora.net><p>Peter S. Shenkin wrote:<br>>I know I'm being dense here, but could someone explain to me<br>>what problem this person is trying to solve? I've read this<br>>and the FAQ section posted by Dann Corbitt, and I still don't<br>>get it.<br>><br>>Why would you possibly want to discard the user's input, and<br>>how in the world would you know what part to discard?<p>The short answers are: “You don't, and you don't.”<p>What is going on here is a kludge piled on top of an already “bad”(in some sense) program. Instead of fixing the actual <em>problem</em>,certain books that purport to teach C suggest fixing the <em>symptoms</em>.<p>This is a bit like going in to the doctor with incipient pneumoniaand being given a cough suppressant.<p>The root of the problem is the use of <TT>scanf()</TT>. The <TT>scanf()</TT> functionis a large and complex beast that often does something almost butnot quite entirely unlike what you desired.<p>The entire <TT>scanf()</TT> family works by interpreting “directives”. Thesedirectives are not well suited to interactive (“talking to a human”)input. In particular, a directive like “ ” or “<TT>\n</TT>” means: “skip asmuch input white space as you can find, INCLUDING NEWLINES.” Mostconversion directives (including <TT>%d</TT> and <TT>%f</TT>) have an implicit skip aswell. This means that if you print a prompt:<p><pre> printf("please enter a number: "); fflush(stdout);</pre><p>and then ask for input using `<TT>scanf("%d", &var)</TT>', and the human entersa blank line, the computer simply sits there, rather than re-prompting.<p>The next problem is that the <TT>scanf()</TT> family tend to leave unconsumedinput. Anything that does not “meet the requirements” is left inplace. If the user, who is supposed to enter a number, enters“three” instead of “3”, the “t” does not meet the requirements for“<TT>%d</TT>”. The entire line (“three\n”) is left in the input stream.If the user <em>does</em> enter a number, such as “3”, only the newlineis left in the stream. If the user enters a number followed by ablank (or tab or other whitespace), the blank and newline are bothleft in the stream.<p>This characteristic (of leaving “extra” input behind as a trap tothe unwary) leads people to write the “discard user's input” code.Unfortunately, they often use implementation-specific constructs like<TT>fflush(stdin)</TT>, or broken ones like an unadorned <TT>getchar()</TT> or a<TT>scanf</TT> format like “<TT>%*[^\n]%*c</TT>”. To see why this last format is broken,read the next paragraphs.<p>Another substantial problem with the <TT>scanf()</TT> functions is that theyinterpret directives sequentially, and stop as soon as they get a“matching failure”. This seems often to surprise people. Inparticular, consider the format directive “<TT>%*[^\n]</TT>”. This consistsof several parts. The “<TT>%</TT>” introduces the conversion. The asterisk“<TT>*</TT>” suppresses assignment of the result of the conversion, so thatno additional buffer is required. The “<TT>[</TT>” specifies that theconversion is to do a character-class match, the initial “<TT>^</TT>” invertsthe class, and the class itself consists only of a single character,“<TT>\n</TT>”. The “<TT>]</TT>” terminates the class and is the end of that directive.This directive thus means “match things that are not newlines”.<p>The tricky bit here is that any <TT>%[</TT> directive MUST MATCH AT LEASTONE CHARACTER. If it fails to match at least one character, thescan terminates. The call returns without looking at any furtherdirectives. Thus, if the next input character is a newline, this“<TT>%*[</TT>” directive fails, and the “<TT>%*c</TT>” NEVER OCCURS. The formatsequence “<TT>%*[^\n]%*c</TT>” only clears out the rest of a line if thereis at least one character before the newline.<p>This problem can be fixed (as others noted) by using two separatecalls. An initial <TT>scanf()</TT> with “<TT>%*[^\n]</TT>” will either eat everythingup to but not including a newline, or fail. A subsequent “<TT>%*c</TT>”(or plain old <TT>getchar()</TT>) will consume the newline, if there was one.<p>That last “if” matters too: perhaps the user signalled EOF. In thiscase, the <TT>getchar()</TT> or <TT>scanf("%*c")</TT> might -- this decision is leftto the people who write your compiler -- either immediately returnEOF, or go back to the user for more input. If the implementorschoose the latter, the user might have to click on “end this thing”(<TT>^D</TT>, <TT>^Z</TT>, mouse button, front panel switch, or whatever) one extratime. This is annoying, if nothing else.<p>(Incidentally, offhand, I think <TT>%[</TT> and <TT>%c</TT> are the only two directivesthat do not immediately skip whitespace, including newlines.)<p>So what <em>is</em> the “right” answer? There are various ways to dothis. You can write horrendously complicated code using <TT>getchar</TT>,<TT>ungetc</TT>, and <TT>scanf</TT>, carefully checking all the return values. Youcan call <TT>fgets()</TT> to read a complete line, then -- having “sandboxed”it, as it were, use <TT>sscanf()</TT> and not worry too much about bad input.You can call <TT>fgets()</TT> and use <TT>strtol()</TT> and other string-parsingfunctions. The simpler approaches all share one common characteristic,though: they first read a complete line (including the terminatingnewline), and only then try to pick it apart. That gives userstime to mull over their answer, type something in, erase it, typesomething else, erase that, think a bit more, and then give RegisPhilbin their “final answers” by pressing ENTER or RETURN. :-) Youthen get the whole thing at once, and can dissect it as needed.<br>-- <br>In-Real-Life: Chris Torek, Berkeley Software Design Inc<br>El Cerrito, CA, USA<br>http://claw.bsdi.com/torek/ (not always up) I report spam to abuse@.<br>-- <br>comp.lang.c.moderated - moderation address: clcm@plethora.net</body><!-- Mirrored from c-faq.com/stdio/gets_flush1.html by HTTrack Website Copier/3.x [XR&CO'2008], Sat, 14 Mar 2009 08:01:11 GMT --></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -