What is the safest cross-platform way to get the address of a byte in a word?

160 Views Asked by At

The following sets byte to 1 on big-endian and 0 on little-endian.

uint16_t word = 0x0001;
uint8_t byte = *(((uint8_t *)&word) + 1);

Is there any way to get the address of the low or high byte that is cross-platform safe?

2

There are 2 best solutions below

1
On BEST ANSWER

Since C99, code could use a compound literal to find the MSByte address offset.
Let the compiler form efficient code.

Below uses a 4-byte example to help illustrate adherence with big, little and PDP endian.

int main() {
  uint32_t word = 0x12345678;
  printf("%p\n", (void*)&word);
  for (unsigned i=0; i<sizeof word; i++) printf("%x\n", ((uint8_t*) &word)[i]);

  uint8_t *msbyte_address = ((uint8_t*) &word) + //
  //  v----------------------------------------------------v compound literal
      ( union { uint32_t u32; uint8_t u8[4]; }) {0x00010203}.u8[0];
  //                                    value at 1st byte    ^---^

  printf("%p\n", (void*)msbyte_address);
}

Example output (little endian)

0xffffcbfc
78
56
34
12
0xffffcbff

For uint16_t

  uint16_t word = 0x1234;
  uint8_t *msbyte_address = ((uint8_t*) &word) + 
      ( union { uint16_t u16; uint8_t u8[2]; }) {0x0001}.u8[0];
2
On

Maybe like this:

int isBigEndian()
{
    uint16_t word = 0x0001;
    return *(((uint8_t *)&word) + 1);
}

void main()
{
    uint16_t word = 0x0001;
    uint8_t byte = *(((uint8_t *)&word) + isBigEndian());
    printf("%d\n", byte);
}

To avoid checking at run time everytime, you can use a #define and verify it is a correct assumption using assert. Like this:

#define BIG_ENDIAN 0  // 0 or 1 depending on what the platform is

void main()
{
    assert(isBigEndian() == BIG_ENDIAN);  // Make sure #define is OK
    // more code...
}

Elsewhere in the code, you use the symbol BIG_ENDIAN to compile code depending on the platform. So there is no other actual check than the assert.