Renato
Renato

Reputation: 302

Reading elements from a struct with fread()

I'm trying to read the header of a BMP file and then display it's contents.

struct BMP *bmp;
bmp = (struct BMP*)malloc(sizeof(struct BMP));
    if(bmp)
    {
        fread(bmp,sizeof(struct BMP),1,bmpFile); //This does not work for me

        //Then we display the contents
        printf("#######CABECALHO DE UM ARQUIVO .BMP (BITMAP)#########\n");
        printf("\n");
        printf("Tipo de Arquivo (2 bytes): %c%c\n", bmp->id[0],bmp->id[1]);
        printf("Tamanho do arquivo (4 bytes): %d Kb\n",bmp->filesize/1024);
        printf("Reservado1 (2 bytes): %x\n",bmp->reserved[0]);
        printf("Reservado2 (2 bytes): %x\n",bmp->reserved[1]);
        printf("Tamanho do Cabecalho BMP: %d\n",bmp->headersize);
        printf("Tamanho do Info Header (4 bytes): %d\n", bmp->infosize);
        printf("Largura: (4 bytes): %d\n", bmp->width);
        printf("Altura: (4 bytes): %d\n", bmp->height);
        printf("Plane: (2 bytes): %x\n", bmp->plane);
        printf("Bits por Pixel: (2 bytes): %x\n", bmp->bits);
        printf("Compressao: (4 bytes): %d\n", bmp->compression);
        printf("Tamanho da Imagem: (4 bytes): %d\n", bmp->imagesize/1024);
        printf("X: (4 bytes): %d\n", bmp->x);
        printf("Y: (4 bytes): %d\n", bmp->y);
        printf("Nro de Cores : (4 bytes): %d\n", bmp->clrUsed);
        printf("Nro de Cores Importantes : (4 bytes): %d\n", bmp->clrImportant);
        printf("\n");
        printf("#######FIM DO CABECALHO TOTAL DE 50 BYTES#########");
   }

However the only accurate information it shows me is the first 2 bytes all the other values are incorrect.

If instead of reading the whole struct

struct fread(bmp,sizeof(struct BMP),1,bmpFile);

I read each element at a time

        fread(&bmp->id[0],sizeof(char),1,bmpFile);
        fread(&bmp->id[1],sizeof(char),1,bmpFile);
        fread(&bmp->filesize,sizeof(int),1,bmpFile);
        fread(&bmp->reserved[0],sizeof(short),1,bmpFile);
        fread(&bmp->reserved[1],sizeof(short),1,bmpFile);
        fread(&bmp->headersize,sizeof(int),1,bmpFile);
        fread(&bmp->infosize,sizeof(int),1,bmpFile);
        fread(&bmp->width,sizeof(int),1,bmpFile);
        fread(&bmp->height,sizeof(int),1,bmpFile);
        fread(&bmp->plane,sizeof(short),1,bmpFile);
        fread(&bmp->bits,sizeof(short),1,bmpFile);
        fread(&bmp->compression,sizeof(int),1,bmpFile);
        fread(&bmp->imagesize,sizeof(int),1,bmpFile);
        fread(&bmp->x,sizeof(int),1,bmpFile);
        fread(&bmp->y,sizeof(int),1,bmpFile);
        fread(&bmp->clrUsed,sizeof(int),1,bmpFile);
        fread(&bmp->clrImportant,sizeof(int),1,bmpFile);

then all values are displayed correctly... So my question is why is that happening what is different when I read the whole struct at once.

Upvotes: 0

Views: 1855

Answers (4)

Jonatan Goebel
Jonatan Goebel

Reputation: 1139

If you want it to work, even consider that is not correct to read the entire struct, you should be very careful when defining your struct to have absolutely control of the pads.

your struct should look something like this (if you have a 32bit platform).

struct BMP
{
unsigned char id[];
unsigned short filesize_1;  

/* here we need to control the pads to make sure it will fit to our platform.
/ also, the order of msb and lsb may change according to the platform. */

unsigned short filesize_2;  // Now start a new int.
...
}

and later you need to manipulate the data to get the correct values.

But remember, this is platform dependent, and usually do not worth the pain.

Upvotes: 0

Vaaksiainen
Vaaksiainen

Reputation: 238

The problem is in your struct BMP, why not use ready definition, e.g. the one in WinGDI.h

#pragma pack(push,1)
typedef struct tagBITMAPINFOHEADER{
        DWORD      biSize;
        LONG       biWidth;
        LONG       biHeight;
        WORD       biPlanes;
        WORD       biBitCount;
        DWORD      biCompression;
        DWORD      biSizeImage;
        LONG       biXPelsPerMeter;
        LONG       biYPelsPerMeter;
        DWORD      biClrUsed;
        DWORD      biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
#pragma pack(pop)

Upvotes: 2

Anish Ramaswamy
Anish Ramaswamy

Reputation: 2351

That is because the structure was padded. This means that the size of the structure will not be equal to the sum of the sizes of every individual element of the structure. This is done to align the bytes to improve performance.

Section 6.2.6.1 of the C11 standard says:

When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values.

In short, the padding of a structure is unspecified behaviour. This means any implementation can do whatever it wants without documenting it.

Upvotes: 1

unwind
unwind

Reputation: 400129

Probably because of internal padding in the structure, the original attempt is a very bad and unsafe way of doing binary I/O with a struct.

It's best to load the well-known number of bytes into a byte buffer, then decode each field and copying it to the structure in memory.

Also note that you shouldn't cast the return value of malloc() in C.

Upvotes: 3

Related Questions