Reading hexdump, fread

Viewed 155

I have a binary file which has following hex values.

enter image description here

Reading this binary file with proper code should show the following:

enter image description here

highlighted area in green colour from the 1st image represents fields of father, room, attraction, cake which have output values of 1,1,0,1 separately.

I have to figure out what data types are used to print out 1,1,0,1 for the struct variables in my code written below.

I could guess that 1101 comes from hex 'd'. But, I am not sure how it is printed out in 4 different fields.

I know that my question is related to structure padding or bit fields. But, I am still not sure how my code and fread should be amended.

It would be much appreciated if you can help me out with this or provide me with examples or relevant readings.

#define MAX_HAT 9
#include<stdio.h>
#include<stdlib.h>

//struct variables
struct test
{
    short int land;
    float experience;
    char boys;
    short int angle;
    double industry;
    int thread;
    long int shoe;
    float kitty;
    unsigned char price;
    
    //not sure whether this is done correctly
    unsigned int father: 1;
    unsigned int room: 1;
    unsigned int attraction: 1;
    unsigned int cake: 1;
    
    int foot;
    char hat[MAX_HAT];
    char nest;
    float bean;
};

int main(int argc, char **argv)
{
    struct test t1;
    
    FILE *fp;

        //Input Checking Error
        if (argc < 2) {
                fprintf(stderr, "Usage: %s input_file\n", argv[0]);
                exit(1);
        }
    
        //binary file to open for reading
        fp = fopen(argv[1], "rb");
    
        //File Checking Error
        if (fp == NULL){
                fprintf(stderr, "Cannot open the file %s\n", argv[1]);
                exit(1);
        }

    //Print out struct fields
    printf("land, experience, boys, angle, industry, thread, shoe, kitty, price, father, room, attraction, cake, foot, hat, nest, bean \n");

    //Allocate the values into the struct
    while(fread(&t1.land, sizeof(t1.land), 1, fp) == 1)
    {
        fread(&t1.experience, sizeof(t1.experience), 1, fp);
        fread(&t1.boys, sizeof(t1.boys), 1, fp);
        fread(&t1.angle, sizeof(t1.angle), 1, fp);
        fread(&t1.industry, sizeof(t1.industry), 1, fp);
        fread(&t1.thread, sizeof(t1.thread), 1, fp);
        fread(&t1.shoe, sizeof(t1.shoe), 1, fp);
        fread(&t1.kitty, sizeof(t1.kitty), 1, fp);
        fread(&t1.price, sizeof(t1.price), 1, fp);
        fread(&t1.father, sizeof(t1.father), 1, fp);
        fread(&t1.room, sizeof(t1.room), 1, fp);
        fread(&t1.attraction, sizeof(t1.attraction), 1, fp);
        fread(&t1.cake, sizeof(t1.cake), 1, fp);
        fread(&t1.foot, sizeof(t1.foot), 1, fp);
        fread(&t1.hat, sizeof(t1.hat), 1, fp);
        fread(&t1.nest, sizeof(t1.nest), 1, fp);
        fread(&t1.bean, sizeof(t1.bean), 1, fp); 
                
    //Print out the outputs
    printf("%d, %f, %i, %i, %f, %d, %ld, %f, %u, %, %, %, %, %x, %s, %c, %f\n", t1.land, t1.experience, t1.boys, t1.angle, t1.industry, t1.thread, t1.shoe, t1.kitty, t1.price, t1.father, t1.room, t1.attraction, t1.cake,t1.foot, t1.hat, t1.nest, t1.bean);
        
    }

    //close the file
    fclose(fp);

    return 0;
}
1 Answers

The lines

fread(&t1.father, sizeof(t1.father), 1, fp);
fread(&t1.room, sizeof(t1.room), 1, fp);
fread(&t1.attraction, sizeof(t1.attraction), 1, fp);
fread(&t1.cake, sizeof(t1.cake), 1, fp);

are wrong. You cannot use sizeof on a member of a bit-field. Doing so does not make sense, as a bit-field member can be smaller than a byte, but the smallest size that sizeof can return is one byte. Also, the smallest unit that fread can read is one byte, not one bit.

In your question, you state that the "highlighted area" (2 bytes) represents father, room, attraction, cake. However, ISO C only requires the compiler to support bit-fields the size of an int, which is 4 bytes on most platforms. You may want to try to use a uint16_t (requires #include <stdint.h>) instead of a unsigned int as the underlying data type for the bit field, and hope that your compiler supports it. Otherwise, the size of the input data and the size of the bit-field will not match, which would make things more complicated.

Also, another problem is that ISO C does not specify the order in which the bits are stored in the bit-field, i.e. whether the bits are packed from right-to-left or from left-to-right. Therefore, if your input packs the bits from the other side as your compiler, then you will have to compensate for that.

Instead of using a bit-field, it may be easier to use the bitwise-AND operator & to mask out individual bits and the >> operator to shift them. Using these two operators in combination allows you to extract individual bits.

Asssuming that you have a variable a of type uint16_t, you can read two bytes from the input file into that variable using the following code:

if ( fread( &a, sizeof a, 1, fp) != 1 )
    /*handle error*/;

Afterwards, If you assume that father is represented in the least-significant bit, then you can extract the bits and assign them to the appropriate variables using the following code:

bool father     = ( a & (0x01<<0) ) >> 0;
bool room       = ( a & (0x01<<1) ) >> 1;
bool attraction = ( a & (0x01<<2) ) >> 2;
bool cake       = ( a & (0x01<<3) ) >> 3;

Note that to be able to use the data type bool, you must #include <stdbool.h>.

See bitwise operations in C for further information.

If you want to instead solve the problem with a bit-field, then I recommend that you change the lines

unsigned int father: 1;
unsigned int room: 1;
unsigned int attraction: 1;
unsigned int cake: 1;

to the following

uint16_t father: 1;
uint16_t room: 1;
uint16_t attraction: 1;
uint16_t cake: 1;

and hope that your compiler supports it (gcc does) and hope that the compiler packs the bits from the same side as your input.

However, an additional problem is that the address of the bit-field cannot be taken, because it does not necessarily start at a specific address. As a consequence, you cannot pass the address of the bit-field to fread.

Therefore, you must wrap the bit-fields into their own sub-structure, like this:

struct test
{
    [...]

    struct
    {
        uint16_t father: 1;
        uint16_t room: 1;
        uint16_t attraction: 1;
        uint16_t cake: 1;
    } bitfield;

    [...]
};

Now, you still cannot take the address of any of the bit-field members. However, you can take the address of the struct containing them, and pass that to fread.

Related