0

I have the following structure defined ( u8 is typedef for unsigned char)

struct
{
    u8 length_directory_record;
    u8 extended_attribute_record;
    u8 location_of_extend[8];
    union
    {
        u8 bytes[8];
        struct
        {
            long little;
            long big;
        } endian;
    }
} dir;

Now when I read a file into this like this

fseek(myfile, (SECTOR_SIZE*222)+34+34, 0);
fread_s(&dir, sizeof(dir), sizeof(u8), 18, myfile);

I get weird large numbers when I print the data_length value (little one). The value actually are stored as LSB and MSB ( both byte orders), thats why I use the struct in the union.

printf("Data Length of File Section: %u\n", dir.data_length.endian.little);

However when I do the same steps without reading into a struct it works:

union{
    u8 val[4];
    long v;
} value;
fseek(myfile, ((SECTOR_SIZE * 222) + 34 + 34)+10, 0);
fread_s(&value, sizeof(value), sizeof(u8), 4, myfile);
printf("%u\n", value.v);

What is wrong with my first version? Why is the structure incorrectly filled or where is there any problem i do not see here ?

EDIT: Some more information: The file i am reading is a binary file. ((SECTOR_SIZE * 222) + 34 + 34) is the position where the structure starts. I verified this using a Hex Editor (Sector Size is 2048), so the +10 in the second example directly jumps to the offset of the 32bit number stored as LSB an MSB ( so 8 bytes)

File Dump of the offset starting the structure:

30 00 DF 00 00 00 00 00 00 DF 30 C3 0B 00 00 0B C3 30   0.ß......ß0Ã....Ã0

Expected value for the data_length is 770864 bytes but the output at the moment is 3862510 (random value)

timrau
  • 22,578
  • 4
  • 51
  • 64
  • You might consider editing the questions and either adding detail about things like `((SECTOR_SIZE * 222) + 34 + 34)+10`; or perhaps removing such from the formula from the question code completely. Also, it would be appropriate to show the relevant portion of the file (perhaps a debug listing of the file), as well as what the expected printf output should be vs what is (in reality) being output. – Mahonri Moriancumer May 30 '14 at 17:02
  • Sounds like it might be a struct padding issue. Have you verified that `sizeof(dir)` is what you expect? If it's not, try using `#pragma pack(1)` and see if that fixes it. – Mike Holt May 30 '14 at 17:03
  • I agree with @MikeHolt that structure packing is probably the issue here. You can verify this easily by examining the memory of your read within a debugger, but more important (for your education) task is to `printf("%u"\n", sizeof(dir));` -- if packing is the issue, you'll find that structure is larger than the 18 bytes you've assumed (and you might also learn a lesson about the evils of hard coding the value `18` instead of using sizeof to get it!). – mah May 30 '14 at 17:09
  • I added a dump of the 18 bytes from the file – WorldSignia May 30 '14 at 17:10
  • Note that the randomness of what you're reading would also make sense if packing is the issue, because you'd be reading only 18 bytes into a 20 (or larger) byte buffer -- the remaining byte values would be undefined. – mah May 30 '14 at 17:11
  • what is `fread_s()` ? – wildplasser May 30 '14 at 17:21
  • @wildplasser: http://msdn.microsoft.com/en-us/library/hh977173.aspx – Bill Lynch May 30 '14 at 17:23

2 Answers2

1

It is likely that your object is not laid out as you expect. This is often due to the compiler inserting blank space in the middle of your struct so that it can access the struct members in the most efficient manner.

Following is a likely layout of your struct:

struct dir {
    uint8_t length_directory_record;   // Stored at offset [0, 1)
    uint8_t extended_attribute_record; // Stored at offset [1, 2)
    uint8_t location_of_extend[8];     // Stored at offset [2, 10)
    // Implicit padding                // Stored at offset [10, 12)
    union
    {
        uint8_t bytes[8];              // Stored at offset [12, 20)
        struct
        {
            int32_t little;            // Stored at offset [12, 16)
            int32_t big;               // Stored at offset [16, 20)
        } endian;
    };
};

From your code, it appears that you expect the union to be found at offset 10. But it's very likely that it's being found at offset 12, or maybe 16.

You could verify the actual offset of dir.bytes by using:

assert(offsetof(dir, bytes) == 12);

Also, most compilers offer a mechanism to pack a struct so that there is no implicit padding. Generally, you either use #pragma pack or __attribute__((packed)).


We can also simplify this bug, so that it is more obvious to you:

struct object {
    uint8_t a;
    uint32_t b;
};

This object will take up 8 bytes of space, and will have 3 bytes of padding between a and b.

Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
0

This sort of problem often occurs with a structure packing mismatch. The junk between fields is likely due to the writer having 32-bit (or great) aligned fields when the fields themselves are not alignment size. What gets written in between? Random cruft.

It's as if this declaration were in effect:

#pragma pack (1)
struct {
    u8 length_directory_record;
    u8 cruft_area_1[3];
    u8 extended_attribute_record;
    u8 cruft_area_2[3];
    u8 location_of_extend[8];

    ...
wallyk
  • 56,922
  • 16
  • 83
  • 148