Can C23 endianness macros be used to determine the layout of a bit-field?

126 Views Asked by At

I've been reading about the new additions to the C standard, and I've come across macros for determining the compile-time endianness.

However the standard still states that

The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined.

Does that mean the layout of a bit-field can be whatever regardless of the value of __STDC_ENDIAN_NATIVE__?

5

There are 5 best solutions below

0
Lundin On BEST ANSWER

The bit order didn't depend on endianess in the first place. But rather if the first bit in the struct is considered MSB or LSB. For example, some implementation-specific bit-fields might just be 1 byte large.

Common sense says that the first bit ought to be MSB in my opinion, because that's how everyone but IBM enumerates bits, from highest to lowest (7 to 0). But apparently most implementations have it as LSB. You can try this example:

#include <stdio.h>

typedef struct
{
  unsigned bit:1;
} bit_t;

int main (void)
{
  bit_t bit = { .bit=1 };
  unsigned char* byte = (unsigned char*)&bit;

  if(*byte & 0x01)
  {
    puts("bit = LSB");
  }
  else if(*byte & 0x80)
  {
    puts("bit = MSB");
  }
  else
  {
    puts("Completely broken compiler.")
  }
}

I tried it out for various targets on Compiler Explorer:

bit = LSB:

  • x86_64 (gcc/clang/icc/msvc)
  • ARM32 (gcc)
  • ARM64 (gcc)
  • Power64le (clang)
  • AVR (gcc)

bit = MSB:

  • MIPS64 (gcc/clang)
  • Power (gcc)
5
KamilCuk On

Can C23 endianness macros be used to determine the layout of a bit-field?

No

Does that mean the layout of a bit-field can be whatever regardless of the value of __STDC_ENDIAN_NATIVE__?

Yes.

Byte endianness - the order of bytes within words - and bit endianness - the order of bits within a byte - are separate.

1
gulpr On

Endianess only applies to order of bytes in longer types.

Bit order in bitfields does not have anything in common with endianness.

0
Ian Abbott On

Both endianness and the order of placement of a bit-field member within an addressable storage unit are implementation defined, but independent.

Note 1: in the ASCII-art diagrams below, bit numbers within an addressable storage unit, a bit-field member (or padding), or a byte have been written vertically. For example:

2
3

indicates the position of bit 23.

Note 2: In the little-endian diagrams, bit numbers increase from left to right. In the big-endian diagrams, bit numbers increase from right to left.

Consider the following type with two bit-fields of size 20 bits and 12 bits within a single, 32-bit wide, 4-byte addressable storage unit:

struct {
    unsigned foo:20;
    unsigned bar:12;
};

There are four endian/storage possibilities:

  1. Little-endian, packed from bit 0 upwards of the addressable storage unit:

    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
    +---------------------------------------------------------------+
    |              Addressable Storage Unit (32 bits)               |
    |0             0 0             1 1     1 2     2 2             3|
    |0             7 8             5 6     9 0     3 4             1|
    +---------------+---------------+---------------+---------------+
    |     Byte 0    |     Byte 1    |     Byte 2    |     Byte 3    |
    |0             7|0             7|0     3 4     7|0             7|
    +---------------+---------------+-------+-------+---------------+
    |             foo (20 bits)             |     bar (12 bits)     |
    |0             0 0             1 1     1|0     0 0             1|
    |0             7 8             5 6     9|0     3 4             1|
    +---------------------------------------+-----------------------+
    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
    
  2. Little-endian, packed from bit 31 downwards of the addressable storage unit:

    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
    +---------------------------------------------------------------+
    |              Addressable Storage Unit (32 bits)               |
    |0             0 0     1 1     1 1             2 2             3|
    |0             7 8     1 2     5 6             3 4             1|
    +---------------+---------------+---------------+---------------+
    |     Byte 0    |     Byte 1    |     Byte 2    |     Byte 3    |
    |0             7|0     3 4     7|0             7|0             7|
    +---------------+-------+-------+---------------+---------------+
    |     bar (12 bits)     |             foo (20 bits)             |
    |0             0 0     1|0     0 0             1 1             1|
    |0             7 8     1|0     3 4             1 2             9|
    +-----------------------+---------------------------------------+
    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
    
    
  3. Big-endian, packed from bit 0 upwards of the addressable storage unit:

    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
    +---------------------------------------------------------------+
    |              Addressable Storage Unit (32 bits)               |
    |3             2 2     2 1     1 1             0 0             0|
    |1             4 3     0 9     6 5             8 7             0|
    +---------------+---------------+---------------+---------------+
    |     Byte 0    |     Byte 1    |     Byte 2    |     Byte 3    |
    |7             0|7     4 3     0|7             0|7             0|
    +---------------+-------+-------+---------------+---------------+
    |     bar (12 bits)     |             foo (20 bits)             |
    |1             0 0     0|1     1 1             0 0             0|
    |1             4 3     0|9     6 5             8 7             0|
    +-----------------------+---------------------------------------+
    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
    
  4. Big-endian, packed from bit 31 downwards of the addressable storage unit:

    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
    +---------------------------------------------------------------+
    |              Addressable Storage Unit (32 bits)               |
    |3             2 2             1 1     1 1     0 0             0|
    |1             4 3             6 5     2 1     8 7             0|
    +---------------+---------------+---------------+---------------+
    |     Byte 0    |     Byte 1    |     Byte 2    |     Byte 3    |
    |7             0|7             0|7     4 3     0|7             0|
    +---------------+---------------+-------+-----------------------+
    |             foo (20 bits)             |     bar (12 bits)     |
    |1             1 1             0 0     0|1     0 0             0|
    |9             2 1             4 3     0|1     8 7             0|
    +---------------------------------------+-----------------------+
    | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
    
0
harold On

Contrary to the answers here that say there is no connection, there actually is: conventionally bit-field ordering follows the endianness of the platform. You can actually see that in the results in Lundins answer, though it goes unacknowledged there.

There is no useful guarantee in the standard, but that convention is widespread.

There are other aspects of bit-field layout that are less reliable.