A discussion on the caching of C-language standard I/O from a c++primer exercise

Source: Internet
Author: User
Tags posix

Just read the signal that chapter, think processing signal when the sigsetjmp/siglongjmp seems to be similar to abnormal jump, so want to review C + + exception, and then found that I am not fully understand the problem.

The topic is the C + + Primer 5.6.3 section of exercise 5.25, described below:

1. Reads 2 integers from the standard input and outputs the result of dividing the 1th integer by the 2nd integer.

2, if the 2nd integer is 0, throws an exception;

3, catch the exception with a try statement block, in the catch statement for the user output a message, asking whether to enter a new number and re-execute the contents of the TRY statement block.

So I write this code

#include <stdio.h> #include <stdexcept>int main () {int x, Y;while (1) {try {fputs ("Input" numbers: ", stdout) ; scanf ("%d%d", &x, &y); if (y = = 0) throw Std::runtime_error ("divisor is 0!"); printf ("%d/%d =%d\n", x, Y and x/y);} catch (std::exception& e) {fputs (E.what (), stderr); Fputs ("Do you want to re-enter? [ Y/N] ", stdout); char ch = getchar (); if (ch = = ' Y ' | | ch = = ' Y ') continue;} break;} return 0;}

Debug See, under GetChar () add a sentence printf ("%d\n", ch), after re-run, you will find that the printing is ten (acsii code in the newline character ' \ n ' corresponds to 10)

That is, GetChar () does not need to wait for our input to get the character. So how does this line break come from?

Oh, just entered "1 0" after the press ENTER, and then scanf to execute. scanf reads that the 2nd int corresponds to the string portion (' 0 ') and stops reading, that is, ' \ n ' is not read in. The standard I/O library takes a cache policy, and the standard input characters are placed in a string array, such as when I entered 1, space, 0, enter, in the file structure corresponding to the standard input (stdin), its cache (which can be considered as a character array) is like this

' 1 ', ' ', ' 0 ', ' \ n ', ' + ', ' ...

The file structure has a pointer to the current position ( Note: pointers in the following all refer to this pointer by default ), initially pointing to ' 1 ', then scanf, reading the 2nd int, the pointer pointing to ' 0 ', then reading ' 0 ', the pointer moving right, pointing to ' \ n ', not a number, begins parsing scanf read 2 int corresponding to the string "1" and "0" and converts the int into the address of X and Y (&x and &y).

As a result, the pointer is pointing to ' \ n ', when the GetChar () is called, the standard input cache already has characters, then it can be taken directly. Only if the standard input pointer has reached the end of the cache non-' e ' character (that is, the end of the so-called character array style string), the process is blocked and waits for user input, the user's input is populated with the cache, and then GetChar () obtains the character pointed to by the pointer.

Back here, the pointer points to ' \ n ', then GetChar () takes it out and returns, then the pointer moves right. So we need to receive the user's new input characters, which need to be like this

GetChar ();  // Remove the line break you just char ch = getchar ();

If you are familiar with the library function fflush (), it is likely to take fflush (stdin) instead of GetChar (), which means flushing the standard input cache.

Seemingly feasible, however, the standard input differs from standard output (STDOUT) and standard error (STDERR), and after both are flushed, the pointer moves right until the end of the string, and then the character in the right-hand process is output to the screen (though, in other words, the system call is actually printed out). Also different from opening ordinary files (TXT, and so on) of the file*, scouring them will be the string output to the text.

So, where can the standard input be output?

POSIX.1-2001 did not specify the behavior for flushing of input streams, but the behavior was specified in posix.1-2008.

In the POSIX.1-2001 standard, the behavior of flushing the input stream is undefined. Although the POSIX new standard defines its behavior, I don't see it specifically, but under Ubuntu 16.04 gcc 5.4.0, the results compiled with-STD=GNU++11 are not what we expected. Although online can search many C language exam fflush (stdin), or VC6.0 environment (I will not say, point to stop)

It would have been OK to change the code as above, but the best way to be robust is to only judge the 1th character, followed by the characters, such as the command to uninstall the software

I entered the YABCD WUFQ UE This part of the blind press string, only the first letter is Y, but the uninstaller still executes.

So can my program do the same?

Just entered 2 characters, the result not only re-entered some information, but also returned directly.

To analyze the execution process of the program:

1, I entered the yy, at this point from the pointer to the position, the cache character is ' \ n ', ' y ', ' y ';

2, GetChar () reads ' \ n ', 2nd GetChar () reads ' Y ' returns and assigns a value to the character ch, then the IF statement determines if CH is ' y ' or ' y '

3. If statement is true, execute continue, skip the remaining code in the while loop (that is, break;) and re-enter the while loop.

In this case, note that the stdin cache is now ' Y ', and scanf will read according to the formatted string "%d%d", which is the first to read 1 int, what if it encounters a character other than the positive and negative numbers?

Change the scanf of the code to the following, check the return value (the return value of scanf is the number of variables that were successfully formatted to write)

int n = scanf ("%d%d", &x, &y), if (n! = 2) {fprintf (stderr, "scanf actually reads the number of int:%d\n", N); return 1;}

The operation results are as follows

In fact, characters that are outside the number, sign (and white-space characters) are returned because formatting input is illegal.

The specific implementation of printf and scanf, mainly using the C language variable parameter type Va_list, can refer to the C language of the classic textbook "C programming language," the author is Dennis Ritchie (Dennis Ritchie), the father of the father of C language &unix. The 7.3-section variable-length parameter table provides a simplified version of printf implementation.

If you try to do it yourself, you will have a deeper understanding of printf/scanf.

So back to the question, how do we solve it? A natural way to think about it is to read the stdin cache all, just like GetChar (), by adding

 while (GetChar ()! = ' \ n ') { }

But this will have the overhead of calling the function, such as I have entered 10,000 characters, then call GetChar () 10,000 times. Too many function calls, the overhead can not be ignored, because each function call is accompanied by the parameters of the stack, out of the stack, the function stack frame creation and destruction.

But from a performance standpoint, you can take a better approach

Char Buf[bufsiz];  while sizeof (BUF), stdin)) {}

That is to reduce the number of function calls, each time get bufsiz characters, so input 10,000 characters, only need to call the function 10000/bufsiz times.

From a practical point of view, this optimization is not really necessary here, first of all, no one is so bored to enter so many characters, at most accidentally pressed a few letters. For example, hand slide press ENTER key to the Next button to press down. Second, the program itself is very simple, even without considering efficiency.

But it does make sense to know this. Look at the source code is not to repeat the wheel, the repetition of the wheel is not just to repeat the wheel, but to deepen the understanding of the underlying implementation. Now that C + + is selected, you have to face a monster called "efficiency" and have to understand the underlying implementation.

Finally add that the C language standard I/O library on the terminal I/O is the default row buffer, the standard I/O library to switch from the application state to the kernel to call the kernel read/write functions, 10,000 times the cost of user function calls may not be large, However, the cost of 10,000 context switches is not small. The kernel's I/O also has its own set of caches. The so-called line buffer, that is, when entering a newline character, one time I/O all the characters I input/output so far, that is, every read a row (as long as this line is not particularly long) only 1 system calls. (Refer to Advanced Programming for UNIX environments)

So each time a newline character is entered, the keyboard input string is moved to memory at once, and then scanf parsing the string from the beginning.

A discussion on the caching of C-language standard I/O from a c++primer exercise

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.