C-Style Binary I/O

Binary I/O is accomplished through two routines: std::fread and std::fwrite. The syntax for std::fread is:
read_size = std::fread(data_ptr, 1, size, file); 
Size of the data that was read. If this is less than size, an end-of-file or error occurred.

Pointer to a buffer to receive the data being read.

The constant 1. (For the reason behind this constant, see the sidebar.)

Number of bytes to be read.

Input file.

Why 1?

If you look at the documentation for std::fread or std::fwrite, you'll see that it the number of bytes to read is specified as two parameters. These two are multiplied together to determine the number of bytes to actually read. For example, to read 12 bytes you could write:

std::fread(in_file, 3, 4, buffer)

The logic behind this system is that if you are reading in an array, you could specify the size of an element (parameter #2) and the number of elements to read (parameter #4). The function would then return the number of elements read, not the number of bytes.

Since every almost every other standard C and C++ function that deals with memory deals with byte sizes, it's much easier to put a 1 in for parameter #2 and a byte size in for #3 and not have to worry about array elements.

For example:

struct { 
        int     width; 
        int     height; 
} rectangle; 
if (std::fread(<static_cast<char *>&rectangle, 1, 
       sizeof(rectangle), in_file) != sizeof(rectangle)) { 
        std::fprintf(stderr, "Unable to read rectangle\n"); 
        exit (8); 

In this example you are reading in the structure rectangle. The & operator makes the structure into a pointer. The cast static_cast<char *> turns &rectangle into the proper parameter type, and the sizeof operator is used to determine how many bytes to read in as well as to check that the read was successful.

std::fwrite has a calling sequence similar to std::fread:

write_size = std::fwrite(data_ptr, 1, size, file); 

No matter what filename you give Example 16-9, std::fopen can't find it. Why?

Example 16-9: fun-file/fun-file.cpp

#include <cstdio>
#include <cstdlib>
int main(  )
    char            name[100];  /* name of the file to use  */
    std::FILE           *in_file;    /* file for input */
    std::printf("Name? ");
    std::fgets(name, sizeof(name), stdin);
    in_file = std::fopen(name, "r");
    if (in_file == NULL) {
        std::fprintf(stderr, "Could not open file\n");
    std::printf("File found\n");
    return (0);

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.


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.)

Page:  1   2   3   4   5   6   7   8   9   Next 


  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Enterprises are typically overwhelmed with incredible volumes of alerts that lack context and are not easily correlated across their technology stacks and silos. It can be incredibly challenging for large enterprises to detect and resolve incidents across multiple environments. When enterprises do detect challenges, communicating and acting efficiently through the right tools can be a daunting proposition. IT teams can provide real value if they collaborate and use agile methods for enterprise DevOps to move …

  • Live Event Date: June 30, 2016 @ 1:00 p.m. ET / 10:00 a.m. PT There's a disconnect happening in our mobile strategies. We have top-down mandate that our organizations should be "mobile first" and undergo "digital transformations" without a lot of additional insight as to how that should become a reality. In the meantime, members within our organizations inherently understand the benefits of mobile apps -- and, understandably, they want to benefit from them. But the resources to bring an app into existence …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date