1

I'm coding a C program to read and parse a BMP header. The first 2 characters are chars, which I'm printing fine. However, all the other bits are ints in little endian format, so I'm storing them backwards. In hex, I'm getting the right answer. However, when I try to cast them to an int (for readability), I get an invalid number.

bmp.c --

printf("file size: "%02x %02x %02x %02x\n", bmp->size[0], bmp->size[1], bmp->size[2], bmp->size[3]);

printf("file size: "%d\n", bit2int(bmp->size));

bit2int function --

int bit2int(void *thing)
{
    return *(int*)thing;
}

output -- (actual file size is 415,798 bytes)

file size: 00 06 58 36
file size: 911738368

edit 1 -- Function I'm currently using

void storebackwards(void *dst, void *src, int offset, int n) {
    for(int i = n; i > 0; i--)
        memcpy( dst + n - i, src + offset + i - 1, 1);
}
Donovan Jenkins
  • 133
  • 1
  • 13

3 Answers3

2

Maybe

int bit2int(unsigned char *thing)
{
    return thing[3] + 256*thing[2] + 65536*thing[1] + 16777216*thing[0];
}
2

BMP size is in little endian. So you're probably swapping the bytes, thus getting a big endian integer, which is of no use.

Then the cast you're doing is violating the strict aliasing rule and it's not portable.

Here's a small piece of code which tries to be as portable as possible. It reads the first 2 header bytes, then the 4 size bytes, and rebuilds the size, assuming the value is little endian:

#include <stdio.h>
#include <stdint.h>

int main()
{
   FILE *f = fopen("foo.bmp","rb");
   if (f)
  {
   unsigned char header[3] = {'\0'};
   unsigned char size_buffer[4];
   int nb_read = fread(header,2,1,f);
   nb_read = fread(size_buffer,4,1,f);
   uint32_t size = size_buffer[0] + (size_buffer[1]<<8) + 
                   (size_buffer[2]<<16) + (size_buffer[3]<<24);
   printf("header: %s, size %lu\n",header,(unsigned long)size);
   fclose(f);
   }

}
  • the code can be compiled on a big endian architecture without issues
  • the file is opened as binary, so it works on Windows & Linux
  • I have omitted size checks when reading the header for simplicity. It can be added very easily.
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • This is definitely the right approach! One should read the data into `unsigned char` buffers, and use the buffer contents in exactly this manner to construct the actual header fields. I'd use helper functions (one per type), though. In particular, the in-memory `struct bmp_file` or similar, that contains the description of the BMP file, does not need to have the same types and field order as the file does. Trying to construct a `struct` that one can `fread()` from a file directly, is horribly nonportable and prone to break. – Nominal Animal Mar 23 '18 at 22:10
2

I like user1063935' solution but would use the shift operator

int bit2int(unsigned char *thing)
{   
    return thing[3] + thing[2] << 8 + thing[1] << 16 + thing[0] << 24;
}
Werner
  • 281
  • 1
  • 12