Search code examples
cendianness

How can I represent a given memory layout, rather than mathematical value, in C?


Ugh, endianness. The problem is the memory layout of say, 0x65736c6166 will be different on different endians. This is because the number is being defined by value. The 66 on the end of my constant will go in the first byte on little endian systems, and on the last byte on big endian systems, yet the number is the same. How can I define number variables by memory layout rather than by value, so on different endians, their memory layout will stay the same, but their value will be completely different? And it needs to be considered compile-time constant.


Solution

  • How can I define number variables by memory layout rather than by value, so on different endians, their memory layout will stay the same, but their value will be completely different?

    Well, there are only two endianess to handle. You could write a macro function that would convert host byte order to the endianess you want and just use such macro when using the constant.

    #include <assert.h>
    #include <string.h>
    #include <inttypes.h>
    
    // Copied from glibc bits/byteswap.h
    #define bswap_constant_64(x) \
      ((((x) & 0xff00000000000000ull) >> 56)    \
       | (((x) & 0x00ff000000000000ull) >> 40)  \
       | (((x) & 0x0000ff0000000000ull) >> 24)  \
       | (((x) & 0x000000ff00000000ull) >> 8)   \
       | (((x) & 0x00000000ff000000ull) << 8)   \
       | (((x) & 0x0000000000ff0000ull) << 24)  \
       | (((x) & 0x000000000000ff00ull) << 40)  \
       | (((x) & 0x00000000000000ffull) << 56))
    
    // __BYTE_ORDER__ and __ORDER_BYTE_ORDER__ are macros defined by gcc
    // use different check when compiling with a compiler that doesnt define them
    #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
    #define HTOLE64(x)  (x)
    #else
    #define HTOLE64(x)  bswap_constant_64(x)
    #endif
    
    // yes a constant expression
    static unsigned long long mynumber = HTOLE64(0x65736c6166ull);
    
    int main() {
        char bytes[sizeof(mynumber)];
        memcpy(bytes, &mynumber, sizeof(mynumber));
        // works on any endianess
        assert(bytes[0] == 0x66);
        assert(bytes[1] == 0x61);
    }
    

    Tested on godbolt.