Saturday, October 24, 2009

16.12 C- Versus C++- Style I/O




I l@ve RuBoard










16.12 C- Versus C++- Style I/O



Both C- and C++- style I/O have their own features and quirks. In
this section we'll discuss some of the differences
between these two systems.




16.12.1 Simplicity



Let's say we want to write a simple checkbook
program. We need to print an account statement. We need some code to
print each line of the account statement (date, check number, payee,
and amount).



In C the print statement looks like:



std::printf("%2d/%2d/%02d %4d: %-40s %f6.2\n",
check.date.month, check.date.day, check.date.year,
check.number, check.payee, check.amount);


In C++ the print statement is:



std::cout << setw(2) << check.date.month << '/' <<
setw(2) << check.date.day << '/' <<
setw(2) << setfill('0') << check.date.year << ' ' <<
setw(4) << check.number << ':' <<
setw(40) << setiosflags(std::ios::left) <<
check.payee <<
resetiosflags(std::ios::left) << ' ' <<
setw(6) << setprecision(2) <<
setiosflags(std::ios::fixed) <<
check.amount <<
setw(0) << '\n';


From this example we can clearly see that the C-style I/O is more
compact. It is not clear that compact is better. This author prefers
the compact style of the C std::printf functions,
while many others prefer the verbosity of the C++ I/O system. Besides
if you're C++ programmers, you probably should
program in C++ and not bring legacy I/O systems into the mix.



Although it looks like C is more compact, things are not as obvious
as they look. A well-designed date class would
have its own output operator. Thus we can simplify our C++ code down
to:



    std::cout << check.date <<
setw(4) << check.number << ':' <<
setw(40) << setiosflags(std::ios::left) <<
check.payee <<
resetiosflags(std::ios::left) << ' ' <<
setw(6) << setprecision(2) <<
setiosflags(std::ios::fixed) <<
check.amount <<
setw(0) << '\n';


But this assumes that only the date has an output
operator. If we designed our check class correctly, it should have
one as well. This means that our code now has been simplified down
to:



    std::cout << check << '\n';


Now this doesn't mean that complexity has gone away.
It's merely been moved from outside the class to
inside it.



This example serves to illustrate one of the key differences between
C and C++. In C-style I/O, the information on how to manipulate the
data (in this case, how to print it) is contained outside the data
itself. In C++ it's possible to put the manipulation
code and the data into a single class.



If we are writing out our checkbook information in only one place,
the C version may be simpler and easier to work with. So for simple
programs, you may want to consider using C-style I/O. But suppose
that we wanted to print out the data to a number of places. If we
used C-style I/O, we would have to replicate our format code all over
the place or create a small function to do the printing. With C++'s
classes, we can keep the printing information in one logical place.
(As a person who's just had to rewrite all the
C-style format statements in a rather large piece of code, I can tell
you that putting the formatting information in one place, the object,
has some advantages.)





16.12.2 Reliability



When you use C++-style I/O, the system automatically detects the type
of the variable and performs the approbate conversion.
It's impossible to get the types wrong.



With C-style I/O, it's easy to get the arguments to
a std::printf mixed up, resulting in very strange
results when you run the program. What's worse is
that most compilers do not check std::printf calls
and warn you if you make a mistake.



One special C I/O function you should be aware of is
std::gets. This function gets a
line from standard input with no
bounds-checking
. So:



std::gets(line);


is exactly like:



std::fgets(line, INFINITY, stdin);


If there are too many characters in an input line, the
std::gets function will cause a buffer overflow
and trash memory. This single function and its lack of
bounds-checking has to be responsible for more crashes and security
holes than any other single C function.[2] You should never
use it. You can get in enough trouble with the more reliable C
functions without having to play Russian roulette with this one.


[2] As I am
writing this, Microsoft has just released a security patch to Windows
XP to fix a buffer overflow bug.





16.12.3 Speed



I've done some benchmarks on C and C++ I/O for
binary files. In general I've found the C I/O to be
much faster. That's because the C I/O system is less
flexible and has to deal with less overhead than the C++ system.










I'm not talking about formatted I/O, just raw binary
I/O. If you do formatted I/O in either system, you can expect your
speed to go down tremendously. It's the single
slowest system in the entire C and C++ library.






16.12.4 Which Should You Use?



Which I/O system is best? That depends on a large number of factors.
First of all, any system you know is always going to be easier to use
and more reliable than a system you don't know.



However, if you know both systems, C-style I/O is good for the simple
stuff. If you're not doing anything fancy with
classes and just want to write simple formatted reports, the C I/O
system will do the job. However, for larger jobs, the C++-object
oriented system with its object-oriented I/O system handles
complexity and organizes complex information much better than C-style
I/O.



But if you're learning I/O for the first time, I
suggest that you stick with one I/O system, the C++ one. Learn
C-style I/O only if you're forced to. (Say, for
instance, you have to maintain some legacy code that uses the old
C-style system.)










    I l@ve RuBoard



    No comments:

    Post a Comment