cstdio streams vs iostream streams?

一世执手 提交于 2019-11-30 06:12:13

The C and C++ standards make no requirements on how things are implemented, just on what the effect of certain operations is. For the <stdio> vs. <iostream> functionality this means that one could wrap the other, both could be essentially the same, or that they are either entirely independent. Technically, using a common implementation would be ideal for several reasons (e.g. there would be no need for explicit synchronization and there would be a defined mechanism to extend FILE* for user defined systems) but I'm not aware of any system which actually does this. Having one implementation be a wrapper of the other is possible and implementing <iostream>s in terms of <stdio> was a typical implementation choice although it has the drawback that it introduces an extra cost for certain operations and most C++ standard libraries have moved on to use entirely separate implementations.

Unfortunately, both the wrapped and the independent implementation share a common problem: I/O is hideously inefficient when done one character level. Thus, it is essentially mandatory to buffer characters and read from or write to a buffer. This works nicely for streams which are independent of each other. The catch are the standard C streams stdin, stdout, stderr and their C++ narrow character counterparts std::cin, std::cout, std::cerr/std::clog and C++ wide character counterparts std::wcin, std::wcout, std::wcerr/std::wclog, respectively: what happens when a user reads both from stdin and std::cin? If either of these stream read a buffer of characters from the underlying OS stream the reads would appear out of order. Similarly, if both stdout and std::cout used independent buffers characters would appear in unexpected order when a user writes both to both streams. As a result, there are special rules on the standard C++ stream objects (i.e. std::cin, std::cout, std::cerr, and std::clog and their wide character counterparts) which mandate that they synchronize with their respective <stdio> counterpart. Effectively, this means that specifically these C++ objects either use a common implementation directly or that they are implemented in terms of <stdio> and don't buffer any characters.

It was realized that the cost of this synchronization is quite substantial if the implementations don't share a common base and may be unnecessary for some users: if a user only uses <iostream> he doesn't want to pay for the extra indirection and, more importantly, he doesn't want to pay for the extra costs imposed by not using a buffer. For careful implementations the cost of not using a buffer can be quite substantial because it means that certain operations end up having to do a check and possibly a virtual function call in each iteration rather than only once in a while. Thus, std::sync_with_stdio() can be used to turn this synchronization off which may mean that the standard stream objects change their internal implementation more or less entirely. Since the stream buffers of the standard stream objects can be replaced by a user, unfortunately, the stream buffers can't be replaced but the internal implementation of the stream buffer can be changed.

In good implementations of the <iostream> library all this only affects the standard stream objects. That is, file streams should be entirely unaffected by this. However, if you want to use the standard stream objects and want to achieve good performance you clearly don't want to mix <stdio> and <iostream> and you want to turn synchronization off. Especially, when comparing I/O performance between <stdio> and <iostream> you should be aware of this.

Actually the stdout, stderr and stdin are the file handlers of OS. And FILE structure of C as well as iostream classes of C++ are both wrappers of those file handlers. Both iostream classes and FILE structure may have their own buffers or something else that needs to be synchronized between each other to make sure that input from file or output to the file is done correctly.

Okay, here's what I've found.

Actually, the I/O is ultimately performed by native system calls and functions.

Now, take Microsoft Windows for example. There are actually available handles for STDIN , STDIO etc (see here). So basically, both the C++ iostream and C stdio call native system functions, the C++ iostream does not wrap C's I/O functions (in modern implementations). It calls the native system methods directly.

Also, I found this:

Once stdin, stdout, and stderr are redirected, standard C functions such as printf() and gets() can be used, without change, to communicate with the Win32 console. But what about C++ I/O streams? Since cin, cout, cerr, and clog are closely tied to C’s stdin, stdout, and stderr, you would expect them to behave similarly. This is half right.

C++ I/O streams actually come in two flavors: template and non- template. The older non-template version of I/O streams is slowly being replaced by a newer template style of streams first defined by the Standard Template Library (STL) and which are now being absorbed into the ANSI C++ standard. Visual C++ v5 provides both types and allows you to choose between the two by including different header files. STL I/O streams work as you would expect, automatically using any newly redirected stdio handles. Non-template I/O streams, however, do not work as expected. To discover why, I looked at the source code, conveniently provided on the Visual C++ CD-ROM.

The problem is that the older I/O streams were designed to use UNIX-style "file descriptors," where integers are used instead of handles (0 for stdin, 1 for stdout, and so on). That’s convenient for UNIX implementations, but Win32 C compilers have to provide yet another I/O layer to represent that style of I/O, since Win32 does not provide a compatible set of functions. In any case, when you call _open_osfhandle() to associate a new Win32 handle with (for example) stdout, it has no effect on the other layer of I/O code. Hence, file descriptor 1 will continue using the same underlying Win32 handle as before, and sending output to cout will not produce the desired effect.

Fortunately, the designers of the original I/O stream package foresaw this problem and provided a clean and useful solution. The base class ios provides a static function, sync_with_stdio(), that causes the library to change its underlying file descriptors to reflect any changes in the standard I/O layer. Though this is not strictly necessary for STL I/O streams, it does no harm and lets me write code that works correctly with either the new or old form of I/O streams.

(source)

Hence calling sync_with_stdio() actually changes the underlying file descriptors. It was in fact added by the designers to ensure compatibility of the older C++ I/O with systems like Windows-32 which used handles instead of integers.

Note that Using sync_with_stdio() is not necessary with modern C++ template-based STL I/O.

One can be a wrapper around the other (and that works both ways. You could implement stdio functions by using iostream and vice versa. Or you can write them completely independently.

And sync_with_stdio guarantees that the two streams will be synchronized if it's enabled. But they can still synchronize when it's disabled too, if the really want to.

But even if one is a wrapper around the other, one might still have a buffer that the other doesn't share, for example, so that synchronization is still necessary.

They are the same thing, but they might also be buffered separarately. This could affect code that mixes the use of C and C++ I/O, like this

std::cout << "Hello ";
printf("%s", "world");
std::cout << "!\n";

For this to work, the underlying streams must be synchronized somehow. On some systems, this might mean that performance could suffer.

So, the standard allows you to call std::sync_with_stdio(false) to say that you don't care about code like this, but would prefer to have the standard streams work as fast as possible if it makes a difference. On many systems it doesn't make a difference.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!