Search the FAQ Archives

3 - A - B - C - D - E - F - G - H - I - J - K - L - M
N - O - P - Q - R - S - T - U - V - W - X - Y - Z
faqs.org - Internet FAQ Archives

Graphics File Formats FAQ (Part 4 of 4): Tips and Tricks of the Trade
Section - 0. What's the best way to read a file header?

( Part1 - Part2 - Part3 - Part4 - Single Page )
[ Usenet FAQs | Web FAQs | Documents | RFC Index | Neighborhoods ]


Top Document: Graphics File Formats FAQ (Part 4 of 4): Tips and Tricks of the Trade
Previous Document: II. Programming Tips for Graphics File Formats
Next Document: 1. What's this business about endianness?
See reader questions & answers on this topic! - Help others by sharing your knowledge
You wouldn't think there's a lot of mystery about reading a few bytes from
a disk file, eh? Programmer's, however, are constantly loosing time
because they don't consider a few problems that may occur and cause them
to loose time. Consider the following code:

  typedef struct _Header
  {
    BYTE Id;
    WORD Height;
    WORD Width;
    BYTE Colors;
  } HEADER;

  HEADER Header;

  void ReadHeader(FILE *fp)
  {
    if (fp != (FILE *)NULL)
      fread(&Header, sizeof(HEADER), 1, fp);
  }

Looks good, right? The fread() will read the next sizeof(HEADER) bytes from
a valid FILE pointer into the Header data structure. So what could go
wrong?

The problem often encountered with this method is one of element alignment
within structures. Compilers may pad structures with "invisible" elements
to allow each "visible" element to align on a 2- or 4-byte address
boundary.  This is done for efficiency in accessing the element while in
memory. Padding may also be added to the end of the structure to bring
it's total length to an even number of bytes. This is done so the data
following the structure in memory will also align on a proper address
boundary.

If the above code is compiled with no (or 1-byte) structure alignment the
code will operate as expected. With 2-byte alignment an extra two bytes
would be added to the HEADER structure in memory and make it appear as
such:

  typedef struct _Header
  {
    BYTE Id;
    BYTE Pad1;      // Added padding
    WORD Height;
    WORD Width;
    BYTE Colors;
    BYTE Pad2;      // Added padding
  } HEADER;

As you can see the fread() will store the correct value in Id, but the
first byte of Height will be stored in the padding byte. This will throw
off the correct storage of data in the remaining part of the structure
causing the values to be garbage.

A compiler using 4-byte alignment would change the HEADER in memory as such:

  typedef struct _Header
  {
    BYTE Id;
    BYTE Pad1;      // Added padding
    BYTE Pad2;      // Added padding
    BYTE Pad3;      // Added padding
    WORD Height;
    WORD Width;
    BYTE Colors;
    BYTE Pad4;      // Added padding
    BYTE Pad5;      // Added padding
    BYTE Pad6;      // Added padding
  } HEADER;

What started off as a 6-byte header increased to 8 and 12 bytes thanks to
alignment. But what can you do? All the documentation and makefiles you
write will not prevent someone from compiling with the wrong options flag
and then pulling their (or your) hair out when your software appears not
to work correctly.

Now considering this alternative to the ReadHeader() function:

  HEADER Header;

  void ReadHeader(FILE *fp)
  {
    if (fp != (FILE *)NULL)
    {
      fread(&Header.Id, sizeof(Header.Id), 1, fp);
      fread(&Header.Height, sizeof(Header.Height), 1, fp);
      fread(&Header.Width, sizeof(Header.Width), 1, fp);
      fread(&Header.Colors, sizeof(Header.Colors), 1, fp);
    }
  }

What both you and your compiler now see is a lot more code. Rather than
reading the entire structure in one, elegant shot, you read in each
element separately using multiple calls to fread(). The trade-off here is
increased code size for not caring what the structure alignment option of
the compiler is set to. These cases are also true for writing structures
to files using fwrite(). Write only the data and not the padding please.

But is there still anything we've yet over looked? Will fread() (fscanf(),
fgetc(), and so forth) always return the data we expect?  Will fwrite()
(fprintf(), fputc(), and so forth) ever write data that we don't want, or
in a way we don't expect? Read on to the next section...

User Contributions:

Comment about this article, ask questions, or add new information about this topic:

CAPTCHA




Top Document: Graphics File Formats FAQ (Part 4 of 4): Tips and Tricks of the Trade
Previous Document: II. Programming Tips for Graphics File Formats
Next Document: 1. What's this business about endianness?

Part1 - Part2 - Part3 - Part4 - Single Page

[ Usenet FAQs | Web FAQs | Documents | RFC Index ]

Send corrections/additions to the FAQ Maintainer:
jdm@ora.com (James D. Murray)





Last Update March 27 2014 @ 02:11 PM