user13783520
user13783520

Reputation:

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.

Upvotes: 1

Views: 135

Answers (2)

KamilCuk
KamilCuk

Reputation: 141030

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.

Upvotes: 0

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215259

While it's not an integer constant expression, you can define for example:

#define X ((union { unsigned char r[8]; uint64_t v; }){ 0x65, 0x73, 0x6c, 0x61, 0x66 }.v)

and X now expands to an expression of type uint64_t defined in terms of its representation not its value.

Upvotes: 1

Related Questions