Search code examples
cendiannessunionstype-punning

Endianness conversion without relying on undefined behavior


I am using C to read a .png image file, and if you're not familiar with the PNG encoding format, useful integer values are encoded in .png files in the form of 4-byte big-endian integers.

My computer is a little-endian machine, so to convert from a big-endian uint32_t that I read from the file with fread() to a little-endian one my computer understands, I've been using this little function I wrote:

#include <stdint.h>

uint32_t convertEndian(uint32_t val){
  union{
    uint32_t value;
    char bytes[sizeof(uint32_t)];
  }in,out;
  in.value=val;
  for(int i=0;i<sizeof(uint32_t);++i)
    out.bytes[i]=in.bytes[sizeof(uint32_t)-1-i];
  return out.value;
}

This works beautifully on my x86_64 UNIX environment, gcc compiles without error or warning even with the -Wall flag, but I feel rather confident that I'm relying on undefined behavior and type-punning that may not work as well on other systems.

Is there a standard function I can call that can reliably convert a big-endian integer to one the native machine understands, or if not, is there an alternative safer way to do this conversion?


Solution

  • I see no real UB in OP's code.

    Portability issues: yes.

    "type-punning that may not work as well on other systems" is not a problem with OP's C code yet may cause trouble with other languages.


    Yet how about a big (PNG) endian to host instead?

    Extract the bytes by address (lowest address which has the MSByte to highest address which has the LSByte - "big" endian) and form the result with the shifted bytes.

    Something like:

    uint32_t Endian_BigToHost32(uint32_t val) {
      union {
        uint32_t u32;
        uint8_t u8[sizeof(uint32_t)]; // uint8_t insures a byte is 8 bits.
      } x = { .u32 = val };
      return 
          ((uint32_t)x.u8[0] << 24) |
          ((uint32_t)x.u8[1] << 16) |
          ((uint32_t)x.u8[2] <<  8) |
                     x.u8[3];
    }
    

    Tip: many libraries have a implementation specific function to efficiently to this. Example be32toh.