1

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.

  • Generally you don't. Why exactly do you think you need to do this? What problem are you trying to solve that you think this will answer? – dbush Jul 21 '20 at 03:38
  • @dbush I need to control the memory layout of a variable. I am using binary operations were the memory layout of the value is what matters, not the mathematical evaluation of the value. –  Jul 21 '20 at 03:40
  • 2
    "And it needs to be considered compile-time constant." --> Why. Post its use. – chux - Reinstate Monica Jul 21 '20 at 03:43
  • @chux-ReinstateMonica Comparing [memory representations of strings](https://stackoverflow.com/questions/63006673/whats-the-proper-way-to-copy-a-char-array-of-a-given-size-to-an-integer-in-c) against values in switch cases. –  Jul 21 '20 at 04:03
  • 1
    Hmmm, [`Tfalse`](https://stackoverflow.com/a/63005000/2410359) is a compile-time constant – chux - Reinstate Monica Jul 21 '20 at 04:17
  • @chux-ReinstateMonica Thanks! –  Jul 21 '20 at 04:24
  • It would be easier if you would actually post the code and explain how do you want to use that value. – KamilCuk Jul 21 '20 at 07:25
  • @chux-ReinstateMonica But is it endian-independant? –  Jul 25 '20 at 23:23

2 Answers2

1

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.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Is that considered compile-time constant? –  Jul 21 '20 at 03:41
  • @user13783520: Depends on what you mean by that. – R.. GitHub STOP HELPING ICE Jul 21 '20 at 03:43
  • *6.6 Constant expressions ¶8* defines an arithmetic constant expression in a way that excludes expressions containing compound literals, so this means it's not valid for use in initializers of objects with static storage duration unless the implementation allows them as an extension. But if you just care whether it will be evaluated at compile time in contexts where it's already legal (i.e. if you just care about performance), for all practical purposes the answer is yes. – R.. GitHub STOP HELPING ICE Jul 21 '20 at 03:46
  • It needs to be compile-time constant like what's accepted as a static initializer switch case etc. –  Jul 21 '20 at 03:57
  • What is "ice" that GitHub should stop helping? –  Jul 21 '20 at 14:41
0

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.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • That works, but is there a simpler and more portable way to do that? –  Jul 25 '20 at 23:17
  • I suspect if there would be, there also would be an answer here with such way. Using [endian.h](https://sites.uclouvain.be/SystInfo/usr/include/endian.h.html) is the way such problems got solved in C - just here `HTOLE64` is a compile time constant. – KamilCuk Jul 26 '20 at 06:37